The original source code to the JavaScript Interpreter was provided by Netscape Communications Corporation and the modifications to the source code are derived, directly or indirectly, from such code.  The changes to the original code from Netscape Communications Corporation are documented in the accompanying Readme file. 

--------------------------------------------------------------
**** Start of js.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS shell.
 */
#include "jsstddef.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
/* Removed by JSIFY: #include "prlog.h" */
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"

#ifdef PERLCONNECT
#include "jsperl.h"
#endif

#ifdef LIVECONNECT
#include "jsjava.h"
#endif

#ifdef JSDEBUGGER
#include "jsdebug.h"
#ifdef JSDEBUGGER_JAVA_UI
#include "jsdjava.h"
#endif /* JSDEBUGGER_JAVA_UI */
#ifdef JSDEBUGGER_C_UI
#include "jsdb.h"
#endif /* JSDEBUGGER_C_UI */
#endif /* JSDEBUGGER */

#ifdef XP_UNIX
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif

#ifdef XP_PC
#include <io.h>     /* for isatty() */
#endif

FILE *gErrFile = NULL;
FILE *gOutFile = NULL;

#ifdef XP_MAC
#ifdef MAC_TEST_HACK
/* this is the data file that all Print strings will be echoed into */
FILE *gTestResultFile = NULL;
#define isatty(f) 0
#else
#define isatty(f) 1
#endif

#include <SIOUX.h>
#include <MacTypes.h>

static char* mac_argv[] = { "js", NULL };

static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv)
{
	SIOUXSettings.autocloseonquit = true;
	SIOUXSettings.asktosaveonclose = false;
	/* SIOUXSettings.initializeTB = false;
	 SIOUXSettings.showstatusline = true;*/
	puts(startupMessage);
	SIOUXSetTitle(consoleName);

	/* set up a buffer for stderr (otherwise it's a pig). */
	setvbuf(stderr, malloc(BUFSIZ), _IOLBF, BUFSIZ);

	*argc = 1;
	*argv = mac_argv;
}

#endif

#ifndef JSFILE
# error "JSFILE must be defined for this module to work."
#endif

#ifdef JSDEBUGGER
static JSDContext *_jsdc;
#ifdef JSDEBUGGER_JAVA_UI
static JSDJContext *_jsdjc;
#endif /* JSDEBUGGER_JAVA_UI */
#endif /* JSDEBUGGER */

static int reportWarnings;

typedef enum JSShellErrNum {
#define MSG_DEF(name, number, count, exception, format) \
    name = number,
#include "jsshell.msg"
#undef MSG_DEF
    JSShellErr_Limit
#undef MSGDEF
} JSShellErrNum;

static void
Process(JSContext *cx, JSObject *obj, char *filename)
{
    JSTokenStream *ts;
    JSCodeGenerator cg;
    JSBool ok;
    JSScript *script;
    jsval result;
    JSString *str;

    ts = js_NewFileTokenStream(cx, filename, stdin);
    if (!ts)
	goto out;
#ifdef JSDEBUGGER
    if (!filename)
	ts->filename = "typein";
#endif
    if (isatty(fileno(ts->file))) {
	ts->flags |= TSF_INTERACTIVE;
    } else {
	/* Support the UNIX #! shell hack; gobble the first line if it starts
	 * with '#'.  TODO - this isn't quite compatible with sharp variables,
	 * as a legal js program (using sharp variables) might start with '#'.
	 * But that would require multi-character lookahead.
	 */
	char ch = fgetc(ts->file);
	if (ch == '#') {
	    while((ch = fgetc(ts->file)) != EOF) {
		if(ch == '\n' || ch == '\r')
		    break;
	    }
	}
	ungetc(ch, ts->file);
    }

    do {
	js_InitCodeGenerator(cx, &cg, ts->filename, ts->lineno, ts->principals);
	do {
	    if (ts->flags & TSF_INTERACTIVE)
		fprintf(gOutFile, "js> ");
	    ok = js_CompileTokenStream(cx, obj, ts, &cg);
	    if (ts->flags & TSF_ERROR) {
		ts->flags &= ~TSF_ERROR;
		CLEAR_PUSHBACK(ts);
		ok = JS_TRUE;
	    }
	} while (ok && !(ts->flags & TSF_EOF) && CG_OFFSET(&cg) == 0);
	if (ok) {
	    /* Clear any pending exception from previous failed compiles.  */
	    JS_ClearPendingException(cx);

	    script = js_NewScriptFromCG(cx, &cg, NULL);
	    if (script) {
                JSErrorReporter older;
                
		ok = JS_ExecuteScript(cx, obj, script, &result);
		if (ok &&
		    (ts->flags & TSF_INTERACTIVE) &&
		    result != JSVAL_VOID) {
                    /*
                     * If JS_ValueToString generates an error, suppress
                     * the report and print the exception below.
                     */
                    older = JS_SetErrorReporter(cx, NULL);
		    str = JS_ValueToString(cx, result);
                    JS_SetErrorReporter(cx, older);

		    if (str)
			fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
                    else
                        ok = JS_FALSE;
		}

#if JS_HAS_ERROR_EXCEPTIONS
#if 0
                /*
                 * Require that any time we return failure, an exception has
                 * been set.
                 */
                JS_ASSERT(ok || JS_IsExceptionPending(cx));

                /*
                 * Also that any time an exception has been set, we've
                 * returned failure.
                 */
                JS_ASSERT(!JS_IsExceptionPending(cx) || !ok);
#endif
#endif /* JS_HAS_ERROR_EXCEPTIONS */
		JS_DestroyScript(cx, script);
	    }
	}
	cg.firstLine = ts->lineno;
	js_FinishCodeGenerator(cx, &cg);
	RESET_TOKENBUF(ts);
    } while (!(ts->flags & TSF_EOF));

out:
    if (ts)
	(void) js_CloseTokenStream(cx, ts);
    JS_FreeArenaPool(&cx->codePool);
    JS_FreeArenaPool(&cx->tempPool);
}

static int
usage(void)
{
    fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
    fprintf(gErrFile, "usage: js [-w] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n");
    return 2;
}

static int
ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
{
    int i;
    char *filename = NULL;
    jsint length;
    jsval *vector;
    jsval *p;
    JSObject *argsObj;
    JSBool isInteractive = JS_TRUE;

    for (i=0; i < argc; i++) {
	if (argv[i][0] == '-') {
	    switch (argv[i][1]) {
	    case 'v':
		if (i+1 == argc) {
		    return usage();
		}
		JS_SetVersion(cx, atoi(argv[i+1]));
		i++;
		break;
	    case 'w':
		reportWarnings++;
		break;

	    case 'f':
		if (i+1 == argc) {
		    return usage();
		}
		filename = argv[i+1];
		/* "-f -" means read from stdin */
		if (filename[0] == '-' && filename[1] == '\0')
		    filename = NULL;
		Process(cx, obj, filename);
                filename = NULL;
                /* XXX: js -f foo.js should interpret foo.js and then
                 * drop into interactive mode, but that breaks test
                 * harness. Just execute foo.js for now. 
                 */
                isInteractive = JS_FALSE;
		i++;
		break;
	    default:
		return usage();
	    }
	} else {
	    filename = argv[i++];
            isInteractive = JS_FALSE;
	    break;
	}
    }

    length = argc - i;
    vector = JS_malloc(cx, length * sizeof(jsval));
    p = vector;

    if (vector == NULL)
	return 1;

    while (i < argc) {
	JSString *str = JS_NewStringCopyZ(cx, argv[i]);
	if (str == NULL)
	    return 1;
	*p++ = STRING_TO_JSVAL(str);
	i++;
    }
    argsObj = JS_NewArrayObject(cx, length, vector);
    JS_free(cx, vector);
    if (argsObj == NULL)
	return 1;

    if (!JS_DefineProperty(cx, obj, "arguments",
			   OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0))
	return 1;

    if (filename || isInteractive)
        Process(cx, obj, filename);
    return 0;
}


static JSBool
Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (argc > 0 && JSVAL_IS_INT(argv[0]))
	*rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVAL_TO_INT(argv[0])));
    else
	*rval = INT_TO_JSVAL(JS_GetVersion(cx));
    return JS_TRUE;
}

static void
my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);

static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);

static const JSErrorFormatString *
my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);

static JSBool
Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN i;
    JSString *str;
    const char *filename;
    JSScript *script;
    JSBool ok;
    jsval result;
    JSErrorReporter older;

    for (i = 0; i < argc; i++) {
	str = JS_ValueToString(cx, argv[i]);
	if (!str)
	    return JS_FALSE;
	argv[i] = STRING_TO_JSVAL(str);
	filename = JS_GetStringBytes(str);
	errno = 0;
        older = JS_SetErrorReporter(cx, my_LoadErrorReporter);
	script = JS_CompileFile(cx, obj, filename);
	if (!script)
            ok = JS_FALSE;
        else {
            ok = JS_ExecuteScript(cx, obj, script, &result);
	    JS_DestroyScript(cx, script);
        }
        JS_SetErrorReporter(cx, older);
	if (!ok)
	    return JS_FALSE;
    }

    return JS_TRUE;
}

static JSBool
Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN i, n;
    JSString *str;

    for (i = n = 0; i < argc; i++) {
	str = JS_ValueToString(cx, argv[i]);
	if (!str)
	    return JS_FALSE;
	fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));
    }
    n++;
    if (n)
        fputc('\n', gOutFile);
    return JS_TRUE;
}

static JSBool
Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

static JSBool
Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#ifdef LIVECONNECT
    JSJ_SimpleShutdown();
#endif
    exit(0);
    return JS_FALSE;
}

#ifdef GC_MARK_DEBUG
extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
#endif

static JSBool
GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSRuntime *rt;
    uint32 preBytes;

    rt = cx->runtime;
    preBytes = rt->gcBytes;
#ifdef GC_MARK_DEBUG
    if (argc && JSVAL_IS_STRING(argv[0])) {
	char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
	FILE *file = fopen(name, "w");
	if (!file) {
	    fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
	    return JS_FALSE;
	}
	js_DumpGCHeap = file;
    } else {
	js_DumpGCHeap = stdout;
    }
#endif
    js_ForceGC(cx);
#ifdef GC_MARK_DEBUG
    if (js_DumpGCHeap != stdout)
	fclose(js_DumpGCHeap);
    js_DumpGCHeap = NULL;
#endif
    fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
	   (unsigned long)preBytes, (unsigned long)rt->gcBytes,
#ifdef XP_UNIX
	   (unsigned long)sbrk(0)
#else
	   0
#endif
	   );
#ifdef JS_GCMETER
    js_DumpGCStats(rt, stdout);
#endif
    return JS_TRUE;
}

static JSBool
GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
	    int32 *ip)
{
    uintN intarg;
    JSFunction *fun;

    *scriptp = cx->fp->down->script;
    *ip = 0;
    if (argc != 0) {
	intarg = 0;
	if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) {
	    fun = JS_ValueToFunction(cx, argv[0]);
	    if (!fun)
		return JS_FALSE;
	    *scriptp = fun->script;
	    intarg++;
	}
	if (argc > intarg) {
	    if (!JS_ValueToInt32(cx, argv[intarg], ip))
		return JS_FALSE;
	}
    }
    return JS_TRUE;
}

static JSTrapStatus
TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
	    void *closure)
{
    JSString *str;
    JSStackFrame *caller;

    str = closure;
    caller = cx->fp->down;
    if (!JS_EvaluateScript(cx, caller->scopeChain,
			   JS_GetStringBytes(str), JS_GetStringLength(str),
			   caller->script->filename, caller->script->lineno,
			   rval)) {
	return JSTRAP_ERROR;
    }
    if (*rval != JSVAL_VOID)
	return JSTRAP_RETURN;
    return JSTRAP_CONTINUE;
}

static JSBool
Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    JSScript *script;
    int32 i;

    if (argc == 0) {
	JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
	return JS_FALSE;
    }
    argc--;
    str = JS_ValueToString(cx, argv[argc]);
    if (!str)
	return JS_FALSE;
    argv[argc] = STRING_TO_JSVAL(str);
    if (!GetTrapArgs(cx, argc, argv, &script, &i))
	return JS_FALSE;
    return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
}

static JSBool
Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSScript *script;
    int32 i;

    if (!GetTrapArgs(cx, argc, argv, &script, &i))
	return JS_FALSE;
    JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
    return JS_TRUE;
}

static JSBool
LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSScript *script;
    int32 i;
    uintN lineno;
    jsbytecode *pc;

    if (argc == 0) {
	JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
	return JS_FALSE;
    }
    script = cx->fp->down->script;
    if (!GetTrapArgs(cx, argc, argv, &script, &i))
	return JS_FALSE;
    lineno = (i == 0) ? script->lineno : (uintN)i;
    pc = JS_LineNumberToPC(cx, script, lineno);
    if (!pc)
	return JS_FALSE;
    *rval = INT_TO_JSVAL(pc - script->code);
    return JS_TRUE;
}

static JSBool
PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSScript *script;
    int32 i;
    uintN lineno;

    if (!GetTrapArgs(cx, argc, argv, &script, &i))
	return JS_FALSE;
    lineno = JS_PCToLineNumber(cx, script, script->code + i);
    if (!lineno)
	return JS_FALSE;
    *rval = INT_TO_JSVAL(lineno);
    return JS_TRUE;
}

#ifdef DEBUG

static void
SrcNotes(JSContext *cx, JSFunction *fun )
{
    uintN offset, delta, caseOff;
    jssrcnote *notes, *sn;
    JSSrcNoteType type;
    jsatomid atomIndex;
    JSAtom *atom;

    notes = fun->script->notes;
    if (notes) {
	fprintf(gOutFile, "\nSource notes:\n");
	offset = 0;
	for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
	    delta = SN_DELTA(sn);
	    offset += delta;
	    fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
		   sn - notes, offset, delta, js_SrcNoteName[SN_TYPE(sn)]);
	    type = SN_TYPE(sn);
	    switch (type) {
	      case SRC_SETLINE:
		fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
		break;
	      case SRC_FOR:
		fprintf(gOutFile, " cond %u update %u tail %u",
		       (uintN) js_GetSrcNoteOffset(sn, 0),
		       (uintN) js_GetSrcNoteOffset(sn, 1),
		       (uintN) js_GetSrcNoteOffset(sn, 2));
		break;
	      case SRC_PCBASE:
	      case SRC_PCDELTA:
		fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
		break;
	      case SRC_LABEL:
	      case SRC_LABELBRACE:
	      case SRC_BREAK2LABEL:
	      case SRC_CONT2LABEL:
	      case SRC_FUNCDEF:
		atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
		atom = js_GetAtom(cx, &fun->script->atomMap, atomIndex);
		fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, ATOM_BYTES(atom));
		break;
	      case SRC_SWITCH:
		fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));
		caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
		if (caseOff)
		    fprintf(gOutFile, " first case offset %u", caseOff);
		break;
	      case SRC_CATCH:
		delta = (uintN) js_GetSrcNoteOffset(sn, 0);
		if (delta)
		    fprintf(gOutFile, " guard size %u", delta);
		break;
	      default:;
	    }
	    fputc('\n', gOutFile);
	}
    }
}

static JSBool
Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN i;
    JSFunction *fun;

    for (i = 0; i < argc; i++) {
	fun = JS_ValueToFunction(cx, argv[i]);
	if (!fun)
	    return JS_FALSE;

	SrcNotes(cx, fun);
    }
    return JS_TRUE;
}

static JSBool
TryNotes(JSContext *cx, JSFunction *fun)
{
    JSTryNote *tn = fun->script->trynotes;

    if (!tn)
	return JS_TRUE;
    fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");
    while (tn->start && tn->catchStart) {
	fprintf(gOutFile, "  %d\t%d\t%d\n",
	       tn->start, tn->length, tn->catchStart);
	tn++;
    }
    return JS_TRUE;
}

static JSBool
Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSBool lines;
    uintN i;
    JSFunction *fun;

    if (argc > 0 &&
	JSVAL_IS_STRING(argv[0]) &&
	!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
	lines = JS_TRUE;
	argv++, argc--;
    } else {
	lines = JS_FALSE;
    }
    for (i = 0; i < argc; i++) {
	fun = JS_ValueToFunction(cx, argv[i]);
	if (!fun)
	    return JS_FALSE;

	js_Disassemble(cx, fun->script, lines, stdout);
	SrcNotes(cx, fun);
	TryNotes(cx, fun);
    }
    return JS_TRUE;
}

static JSBool
DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#define LINE_BUF_LEN 512
    uintN i, len, line1, line2, bupline;
    JSFunction *fun;
    FILE *file;
    char linebuf[LINE_BUF_LEN];
    jsbytecode *pc, *end;
    static char sep[] = ";-------------------------";

    for (i = 0; i < argc; i++) {
	fun = JS_ValueToFunction(cx, argv[i]);
	if (!fun)
	    return JS_FALSE;

	if (!fun->script || !fun->script->filename) {
	    JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
					    JSSMSG_FILE_SCRIPTS_ONLY);
	    return JS_FALSE;
	}

	file = fopen(fun->script->filename, "r");
	if (!file) {
	    JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
			    JSSMSG_CANT_OPEN,
			    fun->script->filename, strerror(errno));
	    return JS_FALSE;
	}

	pc = fun->script->code;
	end = pc + fun->script->length;

	/* burn the leading lines */
	line2 = JS_PCToLineNumber(cx, fun->script, pc);
	for (line1 = 0; line1 < line2 - 1; line1++)
	    fgets(linebuf, LINE_BUF_LEN, file);

	bupline = 0;
	while (pc < end) {
	    line2 = JS_PCToLineNumber(cx, fun->script, pc);

	    if (line2 < line1) {
		if (bupline != line2) {
		    bupline = line2;
		    fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);
		}
	    } else {
		if (bupline && line1 == line2)
		    fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);
		bupline = 0;
		while (line1 < line2) {
		    if (!fgets(linebuf, LINE_BUF_LEN, file)) {
			JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
				       JSSMSG_UNEXPECTED_EOF,
				       fun->script->filename);
			goto bail;
		    }
		    line1++;
		    fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);
		}
	    }

	    len = js_Disassemble1(cx, fun->script, pc, pc - fun->script->code,
				  JS_TRUE, stdout);
	    if (!len)
		return JS_FALSE;
	    pc += len;
	}

      bail:
	fclose(file);
    }
    return JS_TRUE;
#undef LINE_BUF_LEN
}

static JSBool
Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSBool bval;
    JSString *str;

    if (argc == 0) {
	*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
	return JS_TRUE;
    }

    switch (JS_TypeOfValue(cx, argv[0])) {
      case JSTYPE_NUMBER:
	bval = JSVAL_IS_INT(argv[0])
	       ? JSVAL_TO_INT(argv[0])
	       : (jsint) *JSVAL_TO_DOUBLE(argv[0]);
	break;
      case JSTYPE_BOOLEAN:
	bval = JSVAL_TO_BOOLEAN(argv[0]);
	break;
      default:
	str = JS_ValueToString(cx, argv[0]);
	if (!str)
	    return JS_FALSE;
	fprintf(gErrFile, "tracing: illegal argument %s\n",
		JS_GetStringBytes(str));
	return JS_TRUE;
    }
    cx->tracefp = bval ? stdout : NULL;
    return JS_TRUE;
}

int
DumpAtom(JSHashEntry *he, int i, void *arg)
{
    FILE *fp = arg;
    JSAtom *atom = (JSAtom *)he;

    fprintf(fp, "%3d %08x %5lu ",
	    i, (uintN)he->keyHash, (unsigned long)atom->number);
    if (ATOM_IS_STRING(atom))
	fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom));
    else if (ATOM_IS_INT(atom))
	fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
    else
	fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
    return HT_ENUMERATE_NEXT;
}

int
DumpSymbol(JSHashEntry *he, int i, void *arg)
{
    FILE *fp = arg;
    JSSymbol *sym = (JSSymbol *)he;

    fprintf(fp, "%3d %08x", i, (uintN)he->keyHash);
    if (JSVAL_IS_INT(sym_id(sym)))
	fprintf(fp, " [%ld]\n", (long)JSVAL_TO_INT(sym_id(sym)));
    else
	fprintf(fp, " \"%s\"\n", ATOM_BYTES(sym_atom(sym)));
    return HT_ENUMERATE_NEXT;
}

extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops;

void
DumpScope(JSContext *cx, JSObject *obj, JSHashEnumerator dump, FILE *fp)
{
    JSScope *scope;
    JSSymbol *sym;
    int i;

    fprintf(fp, "\n%s scope contents:\n", OBJ_GET_CLASS(cx, obj)->name);
    scope = (JSScope *)obj->map;
    if (!MAP_IS_NATIVE(&scope->map))
	return;
    if (scope->ops == &js_list_scope_ops) {
	for (sym = (JSSymbol *)scope->data, i = 0; sym;
	     sym = (JSSymbol *)sym->entry.next, i++) {
	    DumpSymbol(&sym->entry, i, fp);
	}
    } else {
	JS_HashTableDump(scope->data, dump, fp);
    }
}

/* These are callable from gdb. */
void Dsym(JSSymbol *sym) { if (sym) DumpSymbol(&sym->entry, 0, gErrFile); }
void Datom(JSAtom *atom) { if (atom) DumpAtom(&atom->entry, 0, gErrFile); }

static JSBool
DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN i;
    JSString *str;
    const char *bytes;
    JSAtom *atom;
    JSObject *obj2;
    JSProperty *prop;
    jsval value;

    for (i = 0; i < argc; i++) {
	str = JS_ValueToString(cx, argv[i]);
	if (!str)
	    return JS_FALSE;
	bytes = JS_GetStringBytes(str);
	if (strcmp(bytes, "arena") == 0) {
#ifdef ARENAMETER
	    JS_DumpArenaStats(stdout);
#endif
	} else if (strcmp(bytes, "atom") == 0) {
	    fprintf(gOutFile, "\natom table contents:\n");
	    JS_HashTableDump(cx->runtime->atomState.table, DumpAtom, stdout);
	} else if (strcmp(bytes, "global") == 0) {
	    DumpScope(cx, cx->globalObject, DumpSymbol, stdout);
	} else {
	    atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
	    if (!atom)
		return JS_FALSE;
	    if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop))
		return JS_FALSE;
	    if (prop) {
		OBJ_DROP_PROPERTY(cx, obj2, prop);
		if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value))
		    return JS_FALSE;
	    }
	    if (!prop || !JSVAL_IS_OBJECT(value)) {
		fprintf(gErrFile, "js: invalid stats argument %s\n",
			bytes);
		continue;
	    }
	    obj = JSVAL_TO_OBJECT(value);
	    if (obj)
		DumpScope(cx, obj, DumpSymbol, stdout);
	}
    }
    return JS_TRUE;
}

#endif /* DEBUG */

#ifdef TEST_EXPORT
static JSBool
DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSAtom *atom;
    JSObject *obj2;
    JSProperty *prop;
    JSBool ok;
    uintN attrs;

    if (argc != 2) {
	JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
	return JS_FALSE;
    }
    if (!JS_ValueToObject(cx, argv[0], &obj))
	return JS_FALSE;
    argv[0] = OBJECT_TO_JSVAL(obj);
    atom = js_ValueToStringAtom(cx, argv[1]);
    if (!atom)
	return JS_FALSE;
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
	return JS_FALSE;
    if (!prop) {
	ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
				 JSPROP_EXPORTED, NULL);
    } else {
	ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
	if (ok) {
	    attrs |= JSPROP_EXPORTED;
	    ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
	}
	OBJ_DROP_PROPERTY(cx, obj2, prop);
    }
    return ok;
}
#endif

#ifdef TEST_CVTARGS
static JSBool
ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSBool b;
    jschar c;
    int32 i, j;
    uint32 u;
    jsdouble d, I;
    char *s;
    JSString *str;
    JSObject *obj;
    JSFunction *fun;
    jsval v;

    if (!JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSofv*",
			     &b, &c, &i, &u, &j, &d, &I, &s, &str, &obj, &fun,
			     &v)) {
	return JS_FALSE;
    }
    fprintf(gOutFile, "b %u, c %x (%c), i %ld, u %lu, j %ld\n", b, c, (char)c, i, u, j);
    fprintf(gOutFile, "d %g, I %g, s %s, S %s, obj %s, fun %s, v %s\n",
	   d, I, s, JS_GetStringBytes(str),
	   JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj))),
	   JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)),
	   JS_GetStringBytes(JS_ValueToString(cx, v)));
    return JS_TRUE;
}
#endif

static JSBool
BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
    return JS_TRUE;
}

static JSFunctionSpec shell_functions[] = {
    {"version",         Version,        0},
    {"load",            Load,           1},
    {"print",           Print,          0},
    {"help",            Help,           0},
    {"quit",            Quit,           0},
    {"gc",              GC,             0},
    {"trap",            Trap,           3},
    {"untrap",          Untrap,         2},
    {"line2pc",         LineToPC,       0},
    {"pc2line",         PCToLine,       0},
#ifdef DEBUG
    {"dis",             Disassemble,    1},
    {"dissrc",          DisassWithSrc,  1},
    {"notes",           Notes,          1},
    {"tracing",         Tracing,        0},
    {"stats",           DumpStats,      1},
#endif
#ifdef TEST_EXPORT
    {"doexp",           DoExport,       2},
#endif
#ifdef TEST_CVTARGS
    {"cvtargs",         ConvertArgs,    0, 0, 12},
#endif
    {"build",           BuildDate,      0},
    {0}
};

/* NOTE: These must be kept in sync with the above. */

static char *shell_help_messages[] = {
    "version [number]       Get or set JavaScript version number",
    "load ['foo.js' ...]    Load files named by string arguments",
    "print [expr ...]       Evaluate and print expressions",
    "help [name ...]        Display usage and help messages",
    "quit                   Quit mocha",
    "gc                     Run the garbage collector",
    "trap [fun] [pc] expr   Trap bytecode execution",
    "untrap [fun] [pc]      Remove a trap",
    "line2pc [fun] line     Map line number to PC",
    "pc2line [fun] [pc]     Map PC to line number",
#ifdef DEBUG
    "dis [fun]              Disassemble functions into bytecodes",
    "dissrc [fun]           Disassemble functions with source lines",
    "notes [fun]            Show source notes for functions",
    "tracing [toggle]       Turn tracing on or off",
    "stats [string ...]     Dump 'arena', 'atom', 'global' stats",
#endif
#ifdef TEST_EXPORT
    "doexp obj id           Export identified property from object",
#endif
#ifdef TEST_CVTARGS
    "cvtargs b c ...        Test JS_ConvertArguments",
#endif
    "build                  Show build date and time",
    0
};

static void
ShowHelpHeader(void)
{
    fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description");
    fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "===========");
}

static void
ShowHelpForCommand(uintN n)
{
    fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);
}

static JSBool
Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN i, j;
    int did_header, did_something;
    JSType type;
    JSFunction *fun;
    JSString *str;
    const char *bytes;

    fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());
    if (argc == 0) {
	ShowHelpHeader();
	for (i = 0; shell_functions[i].name; i++)
	    ShowHelpForCommand(i);
    } else {
	did_header = 0;
	for (i = 0; i < argc; i++) {
	    did_something = 0;
	    type = JS_TypeOfValue(cx, argv[i]);
	    if (type == JSTYPE_FUNCTION) {
		fun = JS_ValueToFunction(cx, argv[i]);
		str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
	    } else if (type == JSTYPE_STRING) {
		str = JSVAL_TO_STRING(argv[i]);
	    } else {
		str = NULL;
	    }
	    if (str) {
		bytes = JS_GetStringBytes(str);
		for (j = 0; shell_functions[j].name; j++) {
		    if (!strcmp(bytes, shell_functions[j].name)) {
			if (!did_header) {
			    did_header = 1;
			    ShowHelpHeader();
			}
			did_something = 1;
			ShowHelpForCommand(j);
			break;
		    }
		}
	    }
	    if (!did_something) {
		str = JS_ValueToString(cx, argv[i]);
		if (!str)
		    return JS_FALSE;
		fprintf(gErrFile, "Sorry, no help for %s\n",
			JS_GetStringBytes(str));
	    }
	}
    }
    return JS_TRUE;
}

/*
 * Define a JS object called "it".  Give it class operations that printf why
 * they're being called for tutorial purposes.
 */
enum its_tinyid {
    ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
};

static JSPropertySpec its_props[] = {
    {"color",           ITS_COLOR,	JSPROP_ENUMERATE},
    {"height",          ITS_HEIGHT,	JSPROP_ENUMERATE},
    {"width",           ITS_WIDTH,	JSPROP_ENUMERATE},
    {"funny",           ITS_FUNNY,	JSPROP_ENUMERATE},
    {"array",           ITS_ARRAY,	JSPROP_ENUMERATE},
    {"rdonly",		ITS_RDONLY,	JSPROP_READONLY},
    {0}
};

#ifdef JSD_LOWLEVEL_SOURCE
/* 
 * This facilitates sending source to JSD (the debugger system) in the shell 
 * where the source is loaded using the JSFILE hack in jsscan. The function 
 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook. 
 * A more normal embedding (e.g. mozilla) loads source itself and can send 
 * source directly to JSD without using this hook scheme.
 */
static void
SendSourceToJSDebugger(const char *filename, uintN lineno,
                       jschar *str, size_t length, 
                       void **listenerTSData, JSDContext* jsdc)
{
    JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;

    if (!jsdsrc) {
        if (!filename)
            filename = "typein";
	if (1 == lineno) {
	    jsdsrc = JSD_NewSourceText(jsdc, filename);
	} else {
	    jsdsrc = JSD_FindSourceForURL(jsdc, filename);
	    if (jsdsrc && JSD_SOURCE_PARTIAL !=
		JSD_GetSourceStatus(jsdc, jsdsrc)) {
		jsdsrc = NULL;
	    }
	}
    }
    if (jsdsrc) {
        jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length, 
                                        JSD_SOURCE_PARTIAL);
    }
    *listenerTSData = jsdsrc;
}
#endif /* JSD_LOWLEVEL_SOURCE */

static JSBool its_noisy;    /* whether to be noisy when finalizing it */

static JSBool
its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (its_noisy) {
	fprintf(gOutFile, "adding its property %s,",
	       JS_GetStringBytes(JS_ValueToString(cx, id)));
	fprintf(gOutFile, " initial value %s\n",
	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));
    }
    return JS_TRUE;
}

static JSBool
its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (its_noisy) {
	fprintf(gOutFile, "deleting its property %s,",
	       JS_GetStringBytes(JS_ValueToString(cx, id)));
	fprintf(gOutFile, " current value %s\n",
	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));
    }
    return JS_TRUE;
}

static JSBool
its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (its_noisy) {
	fprintf(gOutFile, "getting its property %s,",
	       JS_GetStringBytes(JS_ValueToString(cx, id)));
	fprintf(gOutFile, " current value %s\n",
	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));
    }
    return JS_TRUE;
}

static JSBool
its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (its_noisy) {
	fprintf(gOutFile, "setting its property %s,",
	       JS_GetStringBytes(JS_ValueToString(cx, id)));
	fprintf(gOutFile, " new value %s\n",
	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));
    }
    if (JSVAL_IS_STRING(id) &&
	!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
	return JS_ValueToBoolean(cx, *vp, &its_noisy);
    }
    return JS_TRUE;
}

static JSBool
its_enumerate(JSContext *cx, JSObject *obj)
{
    if (its_noisy)
	fprintf(gOutFile, "enumerate its properties\n");
    return JS_TRUE;
}

static JSBool
its_resolve(JSContext *cx, JSObject *obj, jsval id)
{
    if (its_noisy) {
	fprintf(gOutFile, "resolving its property %s\n",
	       JS_GetStringBytes(JS_ValueToString(cx, id)));
    }
    return JS_TRUE;
}

static JSBool
its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
    if (its_noisy)
	fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
    return JS_TRUE;
}

static void
its_finalize(JSContext *cx, JSObject *obj)
{
    if (its_noisy)
	fprintf(gOutFile, "finalizing it\n");
}

static JSClass its_class = {
    "It", 0,
    its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
    its_enumerate,    its_resolve,      its_convert,      its_finalize
};

JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
#if JS_HAS_DFLT_MSG_STRINGS
#define MSG_DEF(name, number, count, exception, format) \
    { format, count } ,
#else
#define MSG_DEF(name, number, count, exception, format) \
    { NULL, count } ,
#endif
#include "jsshell.msg"
#undef MSG_DEF
};

static const JSErrorFormatString *
my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
{
    if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
	    return &jsShell_ErrorFormatString[errorNumber];
	else
	    return NULL;
}

static void
my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
    if (!report) {
        fprintf(gErrFile, "%s\n", message);
        return;
    }

    /* Ignore any exceptions */
    if (JSREPORT_IS_EXCEPTION(report->flags))
        return;

    /* Otherwise, fall back to the ordinary error reporter. */
    my_ErrorReporter(cx, message, report);
}

static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
    int i, j, k, n;
    char *prefix = NULL, *tmp;

    if (!report) {
	fprintf(gErrFile, "%s\n", message);
	return;
    }

    /* Conditionally ignore reported warnings. */
    if ((JSREPORT_IS_WARNING(report->flags) && !reportWarnings))
	return;

    if (report->filename)
	prefix = JS_smprintf("%s:", report->filename);
    if (report->lineno) {
	tmp = prefix;
	prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
	JS_free(cx, tmp);
    }
    if (JSREPORT_IS_WARNING(report->flags)) {
	tmp = prefix;
	prefix = JS_smprintf("%swarning: ", tmp ? tmp : "");
	JS_free(cx, tmp);
    }

    /* embedded newlines -- argh! */
    while ((tmp = strchr(message, '\n')) != 0) {
	tmp++;
	if (prefix) fputs(prefix, gErrFile);
	fwrite(message, 1, tmp - message, gErrFile);
	message = tmp;
    }
    /* If there were no filename or lineno, the prefix might be empty */
    if (prefix)
        fputs(prefix, gErrFile);
    fputs(message, gErrFile);

    if (!report->linebuf) {
	fputc('\n', gErrFile);
	goto out;
    }

    fprintf(gErrFile, ":\n%s%s\n%s", prefix, report->linebuf, prefix);
    n = report->tokenptr - report->linebuf;
    for (i = j = 0; i < n; i++) {
	if (report->linebuf[i] == '\t') {
            for (k = (j + 8) & ~7; j < k; j++) {
		fputc('.', gErrFile);
            }
	    continue;
	}
	fputc('.', gErrFile);
	j++;
    }
    fputs("^\n", gErrFile);
 out:
    JS_free(cx, prefix);
}

#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
static JSBool
Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFunction *fun;
    const char *name, **nargv;
    uintN i, nargc;
    JSString *str;
    pid_t pid;
    int status;

    fun = JS_ValueToFunction(cx, argv[-2]);
    if (!fun)
	return JS_FALSE;
    if (!fun->atom)
	return JS_TRUE;
    name = ATOM_BYTES(fun->atom);
    nargc = 1 + argc;
    nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
    if (!nargv)
	return JS_FALSE;
    nargv[0] = name;
    for (i = 1; i < nargc; i++) {
	str = JS_ValueToString(cx, argv[i-1]);
	if (!str) {
	    JS_free(cx, nargv);
	    return JS_FALSE;
	}
	nargv[i] = JS_GetStringBytes(str);
    }
    nargv[nargc] = 0;
    pid = fork();
    switch (pid) {
      case -1:
	perror("js");
	break;
      case 0:
	(void) execvp(name, (char **)nargv);
	perror("js");
	exit(127);
      default:
	while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
	    ;
	break;
    }
    JS_free(cx, nargv);
    return JS_TRUE;
}
#endif

static JSBool
global_resolve(JSContext *cx, JSObject *obj, jsval id)
{
#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
    /*
     * Do this expensive hack only for unoptimized Unix builds, which are not
     * used for benchmarking.
     */
    char *path, *comp, *full;
    const char *name;
    JSBool ok, found;
    JSFunction *fun;

    if (!JSVAL_IS_STRING(id))
	return JS_TRUE;
    path = getenv("PATH");
    if (!path)
	return JS_TRUE;
    path = JS_strdup(cx, path);
    if (!path)
	return JS_FALSE;
    name = JS_GetStringBytes(JSVAL_TO_STRING(id));
    ok = JS_TRUE;
    for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
	if (*comp != '\0') {
	    full = JS_smprintf("%s/%s", comp, name);
	    if (!full) {
		JS_ReportOutOfMemory(cx);
		ok = JS_FALSE;
		break;
	    }
	} else {
	    full = (char *)name;
	}
	found = (access(full, X_OK) == 0);
	if (*comp != '\0')
	    free(full);
	if (found) {
	    fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);
	    ok = (fun != NULL);
	    break;
	}
    }
    JS_free(cx, path);
    return ok;
#else
    return JS_TRUE;
#endif
}

static JSClass global_class = {
    "global", 0,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, global_resolve,   JS_ConvertStub,   JS_FinalizeStub
};

int
main(int argc, char **argv)
{
    JSVersion version;
    JSRuntime *rt;
    JSContext *cx;
    JSObject *glob, *it;
    int result;
#ifdef LIVECONNECT
    JavaVM *java_vm = NULL;
#endif
#ifdef JSDEBUGGER_JAVA_UI
    JNIEnv *java_env;
#endif

#ifdef XP_OS2
   /* these streams are normally line buffered on OS/2 and need a \n, *
    * so we need to unbuffer then to get a reasonable prompt          */
    setbuf(stdout,0);
    setbuf(stderr,0);
#endif

    gErrFile = stderr;
    gOutFile = stdout;

#ifdef XP_MAC
	initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv);
#endif

#ifdef MAC_TEST_HACK
/*
        Open a file "testArgs.txt" and read each line into argc/argv.
        Re-direct all output to "results.txt"
*/
        {
                char argText[256];
                FILE *f = fopen("testargs.txt", "r");
                if (f != NULL) {
                        int maxArgs = 32; /* arbitrary max !!! */
                        argc = 1;
                        argv = malloc(sizeof(char *) * maxArgs); 
                        argv[0] = NULL;
                        while (fgets(argText, 255, f) != NULL) {
                                 /* argText includes '\n' */
                                argv[argc] = malloc(strlen(argText));
                                strncpy(argv[argc], argText,
                                                    strlen(argText) - 1);
                                argv[argc][strlen(argText) - 1] = '\0';
                                argc++;
                                if (argc >= maxArgs) break;
                        }
                        fclose(f);
                }       
                gTestResultFile = fopen("results.txt", "w");
        }

        gErrFile = gTestResultFile;
        gOutFile = gTestResultFile;
#endif

    version = JSVERSION_DEFAULT;

    argc--;
    argv++;

    rt = JS_NewRuntime(8L * 1024L * 1024L);
    if (!rt)
	return 1;
    cx = JS_NewContext(rt, 8192);
    if (!cx)
	return 1;
    JS_SetErrorReporter(cx, my_ErrorReporter);

    glob = JS_NewObject(cx, &global_class, NULL, NULL);
    if (!glob)
	return 1;
    if (!JS_InitStandardClasses(cx, glob))
	return 1;
    if (!JS_DefineFunctions(cx, glob, shell_functions))
	return 1;

    /* Set version only after there is a global object. */
    if (version != JSVERSION_DEFAULT)
	JS_SetVersion(cx, version);

    it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
    if (!it)
	return 1;
    if (!JS_DefineProperties(cx, it, its_props))
	return 1;

#ifdef PERLCONNECT
    if (!JS_InitPerlClass(cx, glob))
	return 1;
#endif

#ifdef JSDEBUGGER
    /*
    * XXX A command line option to enable debugging (or not) would be good
    */
    _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);
    if (!_jsdc)
	return 1;
    JSD_JSContextInUse(_jsdc, cx);
#ifdef JSD_LOWLEVEL_SOURCE
    JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc);
#endif /* JSD_LOWLEVEL_SOURCE */
#ifdef JSDEBUGGER_JAVA_UI
    _jsdjc = JSDJ_CreateContext();
    if (! _jsdjc)
	return 1;
    JSDJ_SetJSDContext(_jsdjc, _jsdc);
    java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc);
#ifdef LIVECONNECT
    if (java_env)
	(*java_env)->GetJavaVM(java_env, &java_vm);
#endif
    /*
    * XXX This would be the place to wait for the debugger to start.
    * Waiting would be nice in general, but especially when a js file
    * is passed on the cmd line.
    */
#endif /* JSDEBUGGER_JAVA_UI */
#ifdef JSDEBUGGER_C_UI
    JSDB_InitDebugger(rt, _jsdc, 0);
#endif /* JSDEBUGGER_C_UI */
#endif /* JSDEBUGGER */

#ifdef LIVECONNECT
	if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))
	    return 1;
#endif

    result = ProcessArgs(cx, glob, argv, argc);

#ifdef JSDEBUGGER
    if (_jsdc)
	JSD_DebuggerOff(_jsdc);
#endif  /* JSDEBUGGER */

#ifdef MAC_TEST_HACK
    fclose(gTestResultFile);
#endif

    JS_DestroyContext(cx);
    JS_DestroyRuntime(rt);
    JS_ShutDown();
    return result;
}

**** End of js.c. ****

**** Start of jsaddr.c. ****

/* -*- Mode: C; tab-width: 8 -*-
 * Copyright  1996 Netscape Communications Corporation, All Rights Reserved.
 */

#include <stdio.h>
#include <stdlib.h>

#include "jsapi.h"
#include "jsinterp.h"

/* These functions are needed to get the addresses of certain functions
 * in the JS module. On WIN32 especially, these symbols have a different
 * address from the actual address of these functions in the JS module.
 * This is because on WIN32, import function address fixups are done only
 * at load time and function calls are made by indirection - that is by
 * using a couple extra instructions to lookup the actual function address
 * in the importing module's import address table.
 */

JS_EXPORT_API(JSPropertyOp)
js_GetArgumentAddress()
{
	return ((void *)js_GetArgument);
}

JS_EXPORT_API(JSPropertyOp)
js_SetArgumentAddress()
{
	return ((void *)js_SetArgument);
}

JS_EXPORT_API(JSPropertyOp)
js_GetLocalVariableAddress()
{
	return ((void *)js_GetLocalVariable);
}

JS_EXPORT_API(JSPropertyOp)
js_SetLocalVariableAddress()
{
	return ((void *)js_SetLocalVariable);
}

**** End of jsaddr.c. ****

**** Start of jsaddr.h. ****

/* -*- Mode: C; tab-width: 8 -*-
 * Copyright  1996 Netscape Communications Corporation, All Rights Reserved.
 */

#ifndef jsaddr_h___
#define jsaddr_h___

JS_EXTERN_API(JSPropertyOp)
js_GetArgumentAddress();

JS_EXTERN_API(JSPropertyOp)
js_SetArgumentAddress();

JS_EXTERN_API(JSPropertyOp)
js_GetLocalVariableAddress();

JS_EXTERN_API(JSPropertyOp)
js_SetLocalVariableAddress();

#endif /* jsaddr_h___ */

**** End of jsaddr.h. ****

**** Start of jsapi.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JavaScript API.
 */
#include "jsstddef.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsclist.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdate.h"
#include "jsemit.h"
#include "jsexn.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsmath.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsregexp.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

#if JS_HAS_FILE_OBJECT
#include "jsfile.h"
#endif

#if defined(JS_PARANOID_REQUEST) && defined(JS_THREADSAFE)
#define CHECK_REQUEST(cx)	JS_ASSERT(cx->requestDepth)
#else
#define CHECK_REQUEST(cx)	((void)0)
#endif

JS_PUBLIC_API(jsval)
JS_GetNaNValue(JSContext *cx)
{
    CHECK_REQUEST(cx);
    return DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
}

JS_PUBLIC_API(jsval)
JS_GetNegativeInfinityValue(JSContext *cx)
{
    CHECK_REQUEST(cx);
    return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
}

JS_PUBLIC_API(jsval)
JS_GetPositiveInfinityValue(JSContext *cx)
{
    CHECK_REQUEST(cx);
    return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
}

JS_PUBLIC_API(jsval)
JS_GetEmptyStringValue(JSContext *cx)
{
    CHECK_REQUEST(cx);
    return STRING_TO_JSVAL(cx->runtime->emptyString);
}

JS_PUBLIC_API(JSBool)
JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format,
		    ...)
{
    va_list ap;
    uintN i;
    JSBool required;
    const char *cp;
    JSFunction *fun;
    jsdouble d;
    JSString *str;
    JSObject *obj;

    CHECK_REQUEST(cx);
    va_start(ap, format);
    i = 0;
    required = JS_TRUE;
    for (cp = format; *cp != '\0'; cp++) {
	if (isspace(*cp))
	    continue;
	if (*cp == '/') {
	    required = JS_FALSE;
	    continue;
	}
	if (i == argc) {
	    if (required) {
		fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE);
		if (fun) {
		    char numBuf[12];
		    JS_snprintf(numBuf, sizeof numBuf, "%u", argc);
		    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
					 JSMSG_MORE_ARGS_NEEDED,
					 JS_GetFunctionName(fun), numBuf,
					 (argc == 1) ? "" : "s");
		}
		return JS_FALSE;
	    }
	    break;
	}
	switch (*cp) {
	  case 'b':
	    if (!js_ValueToBoolean(cx, argv[i], va_arg(ap, JSBool *)))
		return JS_FALSE;
	    break;
	  case 'c':
	    if (!js_ValueToUint16(cx, argv[i], va_arg(ap, uint16 *)))
		return JS_FALSE;
	    break;
	  case 'i':
	    if (!js_ValueToECMAInt32(cx, argv[i], va_arg(ap, int32 *)))
		return JS_FALSE;
	    break;
	  case 'u':
	    if (!js_ValueToECMAUint32(cx, argv[i], va_arg(ap, uint32 *)))
		return JS_FALSE;
	    break;
	  case 'j':
	    if (!js_ValueToInt32(cx, argv[i], va_arg(ap, int32 *)))
		return JS_FALSE;
	    break;
	  case 'd':
	    if (!js_ValueToNumber(cx, argv[i], va_arg(ap, jsdouble *)))
		return JS_FALSE;
	    break;
	  case 'I':
	    if (!js_ValueToNumber(cx, argv[i], &d))
		return JS_FALSE;
	    *va_arg(ap, jsdouble *) = js_DoubleToInteger(d);
	    break;
	  case 's':
	    str = js_ValueToString(cx, argv[i]);
	    if (!str)
		return JS_FALSE;
	    argv[i] = STRING_TO_JSVAL(str);
	    *va_arg(ap, char **) = JS_GetStringBytes(str);
	    break;
	  case 'S':
	    str = js_ValueToString(cx, argv[i]);
	    if (!str)
		return JS_FALSE;
	    argv[i] = STRING_TO_JSVAL(str);
	    *va_arg(ap, JSString **) = str;
	    break;
	  case 'o':
	    if (!js_ValueToObject(cx, argv[i], &obj))
		return JS_FALSE;
	    argv[i] = OBJECT_TO_JSVAL(obj);
	    *va_arg(ap, JSObject **) = obj;
	    break;
	  case 'f':
	    fun = js_ValueToFunction(cx, &argv[i], JS_FALSE);
	    if (!fun)
		return JS_FALSE;
	    argv[i] = OBJECT_TO_JSVAL(fun->object);
	    *va_arg(ap, JSFunction **) = fun;
	    break;
	  case 'v':
	    *va_arg(ap, jsval *) = argv[i];
	    break;
	  case '*':
	    break;
	  default: {
	    char charBuf[2] = " ";
	    charBuf[0] = *cp;
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR,
				 charBuf);
	    return JS_FALSE;
	    }
	}
	i++;
    }
    va_end(ap);
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp)
{
    JSBool ok = JS_FALSE, b;
    JSObject *obj;
    JSFunction *fun;
    JSString *str;
    jsdouble d, *dp;

    CHECK_REQUEST(cx);
    switch (type) {
      case JSTYPE_VOID:
	*vp = JSVAL_VOID;
	break;
      case JSTYPE_OBJECT:
	ok = js_ValueToObject(cx, v, &obj);
	if (ok)
	    *vp = OBJECT_TO_JSVAL(obj);
	break;
      case JSTYPE_FUNCTION:
	fun = js_ValueToFunction(cx, &v, JS_FALSE);
	ok = (fun != NULL);
	if (ok)
	    *vp = OBJECT_TO_JSVAL(fun->object);
	break;
      case JSTYPE_STRING:
	str = js_ValueToString(cx, v);
	ok = (str != NULL);
	if (ok)
	    *vp = STRING_TO_JSVAL(str);
	break;
      case JSTYPE_NUMBER:
	ok = js_ValueToNumber(cx, v, &d);
	if (ok) {
	    dp = js_NewDouble(cx, d);
	    ok = (dp != NULL);
	    if (ok)
		*vp = DOUBLE_TO_JSVAL(dp);
	}
	break;
      case JSTYPE_BOOLEAN:
	ok = js_ValueToBoolean(cx, v, &b);
	if (ok)
	    *vp = BOOLEAN_TO_JSVAL(b);
	break;
      default: {
	char numBuf[12];
	JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE,
			     numBuf);
	ok = JS_FALSE;
	break;
      }
    }
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
{
    CHECK_REQUEST(cx);
    return js_ValueToObject(cx, v, objp);
}

JS_PUBLIC_API(JSFunction *)
JS_ValueToFunction(JSContext *cx, jsval v)
{
    CHECK_REQUEST(cx);
    return js_ValueToFunction(cx, &v, JS_FALSE);
}

JS_PUBLIC_API(JSFunction *)
JS_ValueToConstructor(JSContext *cx, jsval v)
{
    CHECK_REQUEST(cx);
    return js_ValueToFunction(cx, &v, JS_TRUE);
}

JS_PUBLIC_API(JSString *)
JS_ValueToString(JSContext *cx, jsval v)
{
    CHECK_REQUEST(cx);
    return js_ValueToString(cx, v);
}

JS_PUBLIC_API(JSBool)
JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
{
    CHECK_REQUEST(cx);
    return js_ValueToNumber(cx, v, dp);
}

JS_PUBLIC_API(JSBool)
JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
{
    CHECK_REQUEST(cx);
    return js_ValueToECMAInt32(cx, v, ip);
}

JS_PUBLIC_API(JSBool)
JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
{
    CHECK_REQUEST(cx);
    return js_ValueToECMAUint32(cx, v, ip);
}

JS_PUBLIC_API(JSBool)
JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
{
    CHECK_REQUEST(cx);
    return js_ValueToInt32(cx, v, ip);
}

JS_PUBLIC_API(JSBool)
JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
{
    CHECK_REQUEST(cx);
    return js_ValueToUint16(cx, v, ip);
}

JS_PUBLIC_API(JSBool)
JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
{
    CHECK_REQUEST(cx);
    return js_ValueToBoolean(cx, v, bp);
}

JS_PUBLIC_API(JSType)
JS_TypeOfValue(JSContext *cx, jsval v)
{
    JSType type = JSTYPE_VOID;
    JSObject *obj;
    JSObjectOps *ops;
    JSClass *clasp;

    CHECK_REQUEST(cx);
    if (JSVAL_IS_VOID(v)) {
	type = JSTYPE_VOID;
    } else if (JSVAL_IS_OBJECT(v)) {
	obj = JSVAL_TO_OBJECT(v);
	if (obj &&
	    (ops = obj->map->ops,
	     ops == &js_ObjectOps
	     ? (clasp = OBJ_GET_CLASS(cx, obj),
		clasp->call || clasp == &js_FunctionClass)
	     : ops->call != 0)) {
	    type = JSTYPE_FUNCTION;
	} else {
	    type = JSTYPE_OBJECT;
	}
    } else if (JSVAL_IS_NUMBER(v)) {
	type = JSTYPE_NUMBER;
    } else if (JSVAL_IS_STRING(v)) {
	type = JSTYPE_STRING;
    } else if (JSVAL_IS_BOOLEAN(v)) {
	type = JSTYPE_BOOLEAN;
    }
    return type;
}

JS_PUBLIC_API(const char *)
JS_GetTypeName(JSContext *cx, JSType type)
{
    CHECK_REQUEST(cx);
    if ((uintN)type >= (uintN)JSTYPE_LIMIT)
	return NULL;
    return js_type_str[type];
}

/************************************************************************/

JS_PUBLIC_API(JSRuntime *)
JS_NewRuntime(uint32 maxbytes)
{
    JSRuntime *rt;

#ifdef DEBUG
    JS_BEGIN_MACRO
    /*
     * This code asserts that the numbers associated with the error names in
     * jsmsg.def are monotonically increasing.  It uses values for the error
     * names enumerated in jscntxt.c.  It's not a compiletime check, but it's
     * better than nothing.
     */
    int errorNumber = 0;
#define MSG_DEF(name, number, count, exception, format) \
    JS_ASSERT(name == errorNumber++);
#include "js.msg"
#undef MSG_DEF
    JS_END_MACRO;
#endif /* DEBUG */

    if (!js_InitStringGlobals())
	return NULL;
    rt = malloc(sizeof(JSRuntime));
    if (!rt)
	return NULL;
    memset(rt, 0, sizeof(JSRuntime));
    if (!js_InitGC(rt, maxbytes))
	goto bad;
#ifdef JS_THREADSAFE
    rt->gcLock = JS_NEW_LOCK();
    if (!rt->gcLock)
	goto bad;
    rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
    if (!rt->gcDone)
	goto bad;
    rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
    if (!rt->requestDone)
	goto bad;
    js_SetupLocks(10);		/* this is asymmetric with JS_ShutDown. */
    js_NewLock(&rt->rtLock);
#endif
    rt->propertyCache.empty = JS_TRUE;
    JS_INIT_CLIST(&rt->contextList);
    JS_INIT_CLIST(&rt->trapList);
    JS_INIT_CLIST(&rt->watchPointList);
    return rt;

bad:
    JS_DestroyRuntime(rt);
    return NULL;
}

JS_PUBLIC_API(void)
JS_DestroyRuntime(JSRuntime *rt)
{
    js_FinishGC(rt);
#ifdef JS_THREADSAFE
    if (rt->gcLock)
	JS_DESTROY_LOCK(rt->gcLock);
    if (rt->gcDone)
	JS_DESTROY_CONDVAR(rt->gcDone);
    if (rt->requestDone)
	JS_DESTROY_CONDVAR(rt->requestDone);
    js_DestroyLock(&rt->rtLock);
#endif
    free(rt);
}

JS_PUBLIC_API(void)
JS_ShutDown(void)
{
    js_FreeStringGlobals();
#ifdef JS_THREADSAFE
    js_CleanupLocks();
#endif
}

#ifdef JS_THREADSAFE

JS_PUBLIC_API(void)
JS_BeginRequest(JSContext *cx)
{
    JSRuntime *rt;

    if (!cx->requestDepth) {
	/* Wait until the GC is finished. */
	rt = cx->runtime;
	JS_LOCK_GC(rt);
	while (rt->gcLevel > 0)
	    JS_AWAIT_GC_DONE(rt);

	/* Indicate that a request is running. */
	rt->requestCount++;
	JS_UNLOCK_GC(rt);
    }
    cx->requestDepth++;
}

JS_PUBLIC_API(void)
JS_EndRequest(JSContext *cx)
{
    JSRuntime *rt;

    CHECK_REQUEST(cx);
    cx->requestDepth--;
    if (!cx->requestDepth) {
	rt = cx->runtime;
	JS_LOCK_GC(rt);
	JS_ASSERT(rt->requestCount > 0);
	rt->requestCount--;
	JS_NOTIFY_REQUEST_DONE(rt);
	JS_UNLOCK_GC(rt);
    }
}

/* Yield to pending GC operations, regardless of request depth */
JS_PUBLIC_API(void)
JS_YieldRequest(JSContext *cx)
{
    JSRuntime *rt;

    CHECK_REQUEST(cx);
	rt = cx->runtime;
    JS_ASSERT(rt->requestCount > 0);
    rt->requestCount--;
    JS_NOTIFY_REQUEST_DONE(rt);
    JS_UNLOCK_GC(rt);
    JS_LOCK_GC(rt);
    rt->requestCount++;
    JS_UNLOCK_GC(rt);
}

/* Like JS_EndRequest, but don't notify any GC waiting in the wings. */
JS_PUBLIC_API(void)
JS_SuspendRequest(JSContext *cx)
{
    JSRuntime *rt;

    CHECK_REQUEST(cx);
    cx->requestDepth--;
    if (!cx->requestDepth) {
	rt = cx->runtime;
	JS_LOCK_GC(rt);
	JS_ASSERT(rt->requestCount > 0);
	rt->requestCount--;
	JS_UNLOCK_GC(rt);
    }
}

JS_PUBLIC_API(void)
JS_ResumeRequest(JSContext *cx)
{
    JSRuntime *rt;

    if (!cx->requestDepth) {
	/* Wait until the GC is finished. */
	rt = cx->runtime;
	JS_LOCK_GC(rt);
	while (rt->gcLevel > 0)
	    JS_AWAIT_GC_DONE(rt);

	/* Indicate that a request is running. */
	rt->requestCount++;
	JS_UNLOCK_GC(rt);
    }
    cx->requestDepth++;
}

#endif /* JS_THREADSAFE */

JS_PUBLIC_API(void)
JS_Lock(JSRuntime *rt)
{
    JS_LOCK_RUNTIME(rt);
}

JS_PUBLIC_API(void)
JS_Unlock(JSRuntime *rt)
{
    JS_UNLOCK_RUNTIME(rt);
}

JS_PUBLIC_API(JSContext *)
JS_NewContext(JSRuntime *rt, size_t stacksize)
{
    return js_NewContext(rt, stacksize);
}

JS_PUBLIC_API(void)
JS_DestroyContext(JSContext *cx)
{
    js_DestroyContext(cx);
}

JS_PUBLIC_API(void*)
JS_GetContextPrivate(JSContext *cx)
{
    return cx->data;
}

JS_PUBLIC_API(void)
JS_SetContextPrivate(JSContext *cx, void *data)
{
    cx->data = data;
}

JS_PUBLIC_API(JSRuntime *)
JS_GetRuntime(JSContext *cx)
{
    return cx->runtime;
}

JS_PUBLIC_API(JSContext *)
JS_ContextIterator(JSRuntime *rt, JSContext **iterp)
{
    return js_ContextIterator(rt, iterp);
}

JS_PUBLIC_API(JSVersion)
JS_GetVersion(JSContext *cx)
{
    return cx->version;
}

JS_PUBLIC_API(JSVersion)
JS_SetVersion(JSContext *cx, JSVersion version)
{
    JSVersion oldVersion;

    CHECK_REQUEST(cx);
    oldVersion = cx->version;
    if (version == oldVersion)
        return oldVersion;

    cx->version = version;

#if !JS_BUG_FALLIBLE_EQOPS
    if (cx->version == JSVERSION_1_2) {
	cx->jsop_eq = JSOP_NEW_EQ;
	cx->jsop_ne = JSOP_NEW_NE;
    } else {
	cx->jsop_eq = JSOP_EQ;
	cx->jsop_ne = JSOP_NE;
    }
#endif /* !JS_BUG_FALLIBLE_EQOPS */

#if JS_HAS_EXPORT_IMPORT
    /* XXX this might fail due to low memory */
    js_InitScanner(cx);
#endif /* JS_HAS_EXPORT_IMPORT */

    return oldVersion;
}

JS_PUBLIC_API(const char *)
JS_GetImplementationVersion(void)
{
    return "JavaScript-C 1.4 release 1 1998 10 31";
}


JS_PUBLIC_API(JSObject *)
JS_GetGlobalObject(JSContext *cx)
{
    return cx->globalObject;
}

JS_PUBLIC_API(void)
JS_SetGlobalObject(JSContext *cx, JSObject *obj)
{
    cx->globalObject = obj;
}

JS_PUBLIC_API(JSBool)
JS_InitStandardClasses(JSContext *cx, JSObject *obj)
{
    JSObject *fun_proto, *obj_proto, *array_proto;

    CHECK_REQUEST(cx);
    /* If cx has no global object, use obj so prototypes can be found. */
    if (!cx->globalObject)
	cx->globalObject = obj;

#if JS_HAS_UNDEFINED
    /* Define a top-level property 'undefined' with the undefined value.
     * (proposed ECMA v2.)
     */
    if (!OBJ_DEFINE_PROPERTY(cx, obj,
			     (jsid)cx->runtime->atomState.typeAtoms[JSTYPE_VOID],
			     JSVAL_VOID, NULL, NULL, 0, NULL))
	return JS_FALSE;
#endif

    /* Initialize the function class first so constructors can be made. */
    fun_proto = js_InitFunctionClass(cx, obj);
    if (!fun_proto)
	return JS_FALSE;

    /* Initialize the object class next so Object.prototype works. */
    obj_proto = js_InitObjectClass(cx, obj);
    if (!obj_proto)
	return JS_FALSE;

    /* Function.prototype and the global object delegate to Object.prototype. */
    OBJ_SET_PROTO(cx, fun_proto, obj_proto);
    if (!OBJ_GET_PROTO(cx, obj))
	OBJ_SET_PROTO(cx, obj, obj_proto);

    /* Initialize the rest of the standard objects and functions. */
    return (array_proto = js_InitArrayClass(cx, obj)) != NULL &&
	   js_InitArgsCallClosureClasses(cx, obj, obj_proto) &&
	   js_InitBooleanClass(cx, obj) &&
	   js_InitMathClass(cx, obj) &&
	   js_InitNumberClass(cx, obj) &&
	   js_InitStringClass(cx, obj) &&
#if JS_HAS_REGEXPS
	   js_InitRegExpClass(cx, obj) &&
#endif
#if JS_HAS_SCRIPT_OBJECT
	   js_InitScriptClass(cx, obj) &&
#endif
#if JS_HAS_ERROR_EXCEPTIONS
	   js_InitExceptionClasses(cx, obj) &&
#endif
#if JS_HAS_FILE_OBJECT
           js_InitFileClass(cx, obj) &&
#endif
	   js_InitDateClass(cx, obj);
}

JS_PUBLIC_API(JSObject *)
JS_GetScopeChain(JSContext *cx)
{
    CHECK_REQUEST(cx);
    return cx->fp ? cx->fp->scopeChain : NULL;
}

JS_PUBLIC_API(void *)
JS_malloc(JSContext *cx, size_t nbytes)
{
    void *p;

#if defined(XP_OS2) || defined(XP_MAC) || defined(AIX) || defined(OSF1)
    if (nbytes == 0) /*DSR072897 - Windows allows this, OS/2 & Mac don't*/
	nbytes = 1;
#endif
    p = malloc(nbytes);
    if (!p)
	JS_ReportOutOfMemory(cx);
    return p;
}

JS_PUBLIC_API(void *)
JS_realloc(JSContext *cx, void *p, size_t nbytes)
{
    p = realloc(p, nbytes);
    if (!p)
	JS_ReportOutOfMemory(cx);
    return p;
}

JS_PUBLIC_API(void)
JS_free(JSContext *cx, void *p)
{
    if (p)
	free(p);
}

JS_PUBLIC_API(char *)
JS_strdup(JSContext *cx, const char *s)
{
    char *p = JS_malloc(cx, strlen(s) + 1);
    if (!p)
	return NULL;
    return strcpy(p, s);
}

JS_PUBLIC_API(jsdouble *)
JS_NewDouble(JSContext *cx, jsdouble d)
{
    CHECK_REQUEST(cx);
    return js_NewDouble(cx, d);
}

JS_PUBLIC_API(JSBool)
JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
{
    CHECK_REQUEST(cx);
    return js_NewDoubleValue(cx, d, rval);
}

JS_PUBLIC_API(JSBool)
JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
{
    CHECK_REQUEST(cx);
    return js_NewNumberValue(cx, d, rval);
}

JS_PUBLIC_API(JSBool)
JS_AddRoot(JSContext *cx, void *rp)
{
    CHECK_REQUEST(cx);
    return js_AddRoot(cx, rp, NULL);
}

JS_PUBLIC_API(JSBool)
JS_RemoveRoot(JSContext *cx, void *rp)
{
    CHECK_REQUEST(cx);
    return js_RemoveRoot(cx, rp);
}

JS_PUBLIC_API(JSBool)
JS_AddNamedRoot(JSContext *cx, void *rp, const char *name)
{
    CHECK_REQUEST(cx);
    return js_AddRoot(cx, rp, name);
}

#ifdef DEBUG

#include "jshash.h" /* Added by JSIFY */

typedef struct NamedRootDumpArgs {
    void (*dump)(const char *name, void *rp, void *data);
    void *data;
} NamedRootDumpArgs;

JS_STATIC_DLL_CALLBACK(intN)
js_named_root_dumper(JSHashEntry *he, intN i, void *arg)
{
    NamedRootDumpArgs *args = arg;

    if (he->value)
	args->dump(he->value, (void *)he->key, args->data);
    return HT_ENUMERATE_NEXT;
}

#if XP_MAC //AJFMOD
#pragma export on
#endif

JS_PUBLIC_API(void)
JS_DumpNamedRoots(JSRuntime *rt,
		  void (*dump)(const char *name, void *rp, void *data),
		  void *data)
{
    NamedRootDumpArgs args;

    args.dump = dump;
    args.data = data;
    JS_HashTableEnumerateEntries(rt->gcRootsHash, js_named_root_dumper, &args);
}

#if XP_MAC //AJFMOD
#pragma export reset
#endif

#endif /* DEBUG */

JS_PUBLIC_API(JSBool)
JS_LockGCThing(JSContext *cx, void *thing)
{
    JSBool ok;

    CHECK_REQUEST(cx);
    ok = js_LockGCThing(cx, thing);
    if (!ok)
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_LOCK);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_UnlockGCThing(JSContext *cx, void *thing)
{
    JSBool ok;

    CHECK_REQUEST(cx);
    ok = js_UnlockGCThing(cx, thing);
    if (!ok)
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK);
    return ok;
}

JS_PUBLIC_API(void)
JS_GC(JSContext *cx)
{
    if (!cx->fp)
	JS_FinishArenaPool(&cx->stackPool);
    JS_FinishArenaPool(&cx->codePool);
    JS_FinishArenaPool(&cx->tempPool);
    js_ForceGC(cx);
}

JS_PUBLIC_API(void)
JS_MaybeGC(JSContext *cx)
{
    JSRuntime *rt;
    uint32 bytes, lastBytes;

    rt = cx->runtime;
    bytes = rt->gcBytes;
    lastBytes = rt->gcLastBytes;
    if (bytes > 8192 && bytes > lastBytes + lastBytes / 2)
	JS_GC(cx);
}

JS_PUBLIC_API(JSGCCallback)
JS_SetGCCallback(JSContext *cx, JSGCCallback cb)
{
    JSRuntime *rt;
    JSGCCallback oldcb;

    rt = cx->runtime;
    oldcb = rt->gcCallback;
    rt->gcCallback = cb;
    return oldcb;
}

/************************************************************************/

JS_PUBLIC_API(void)
JS_DestroyIdArray(JSContext *cx, JSIdArray *ida)
{
    JS_free(cx, ida);
}

JS_PUBLIC_API(JSBool)
JS_ValueToId(JSContext *cx, jsval v, jsid *idp)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    if (JSVAL_IS_INT(v)) {
	*idp = v;
    } else {
	atom = js_ValueToStringAtom(cx, v);
	if (!atom)
	    return JS_FALSE;
	*idp = (jsid)atom;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_IdToValue(JSContext *cx, jsid id, jsval *vp)
{
    CHECK_REQUEST(cx);
    *vp = js_IdToValue(id);
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    CHECK_REQUEST(cx);
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_EnumerateStub(JSContext *cx, JSObject *obj)
{
    CHECK_REQUEST(cx);
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id)
{
    CHECK_REQUEST(cx);
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
    CHECK_REQUEST(cx);
#if JS_BUG_EAGER_TOSTRING
    if (type == JSTYPE_STRING)
	return JS_TRUE;
#endif
    return js_TryValueOf(cx, obj, type, vp);
}

JS_PUBLIC_API(void)
JS_FinalizeStub(JSContext *cx, JSObject *obj)
{
}

JS_PUBLIC_API(JSObject *)
JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
	     JSClass *clasp, JSNative constructor, uintN nargs,
	     JSPropertySpec *ps, JSFunctionSpec *fs,
	     JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
{
    JSAtom *atom;
    JSObject *proto, *ctor;
    JSBool named;
    JSFunction *fun;
    jsval junk;

    CHECK_REQUEST(cx);
    atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
    if (!atom)
	return NULL;

    /* Create a prototype object for this class. */
    proto = js_NewObject(cx, clasp, parent_proto, obj);
    if (!proto)
	return NULL;

    if (!constructor) {
	/* Lacking a constructor, name the prototype (e.g., Math). */
	named = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(proto),
				    NULL, NULL, 0, NULL);
	if (!named)
	    goto bad;
	ctor = proto;
    } else {
	/* Define the constructor function in obj's scope. */
	fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0);
	named = (fun != NULL);
	if (!fun)
	    goto bad;

        /* 
         * Remember the class this function is a constructor for so that
         * we know to create an object of this class when we call the 
         * constructor.
         */
        fun->clasp = clasp;

	/* Connect constructor and prototype by named properties. */
	ctor = fun->object;
	if (!js_SetClassPrototype(cx, ctor, proto,
				  JSPROP_READONLY | JSPROP_PERMANENT)) {
	    goto bad;
	}

	/* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
	if (OBJ_GET_CLASS(cx, ctor) == clasp) {
	    /* XXXMLM - this fails in framesets that are writing over
	     *           themselves!
	     * JS_ASSERT(!OBJ_GET_PROTO(cx, ctor));
	     */
	    OBJ_SET_PROTO(cx, ctor, proto);
	}
    }

    /* Add properties and methods to the prototype and the constructor. */
    if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
	(fs && !JS_DefineFunctions(cx, proto, fs)) ||
	(static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
	(static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
	goto bad;
    }
    return proto;

bad:
    if (named)
	(void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, &junk);
    cx->newborn[GCX_OBJECT] = NULL;
    return NULL;
}

#ifdef JS_THREADSAFE
JS_PUBLIC_API(JSClass *)
JS_GetClass(JSContext *cx, JSObject *obj)
{
    CHECK_REQUEST(cx);
    return OBJ_GET_CLASS(cx, obj);
}
#else
JS_PUBLIC_API(JSClass *)
JS_GetClass(JSObject *obj)
{
    CHECK_REQUEST(cx);
    return LOCKED_OBJ_GET_CLASS(obj);
}
#endif

JS_PUBLIC_API(JSBool)
JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv)
{
    JSFunction *fun;

    CHECK_REQUEST(cx);
    if (OBJ_GET_CLASS(cx, obj) == clasp)
	return JS_TRUE;
    if (argv) {
	fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE);
	if (fun) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_INCOMPATIBLE_PROTO,
				 clasp->name, JS_GetFunctionName(fun),
				 OBJ_GET_CLASS(cx, obj)->name);
	}
    }
    return JS_FALSE;
}

JS_PUBLIC_API(void *)
JS_GetPrivate(JSContext *cx, JSObject *obj)
{
    jsval v;

    CHECK_REQUEST(cx);
    JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE);
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_INT(v))
	return NULL;
    return JSVAL_TO_PRIVATE(v);
}

JS_PUBLIC_API(JSBool)
JS_SetPrivate(JSContext *cx, JSObject *obj, void *data)
{
    CHECK_REQUEST(cx);
    JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE);
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data));
    return JS_TRUE;
}

JS_PUBLIC_API(void *)
JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp,
		      jsval *argv)
{
    CHECK_REQUEST(cx);
    if (!JS_InstanceOf(cx, obj, clasp, argv))
	return NULL;
    return JS_GetPrivate(cx, obj);
}

JS_PUBLIC_API(JSObject *)
JS_GetPrototype(JSContext *cx, JSObject *obj)
{
    JSObject *proto;

    CHECK_REQUEST(cx);
    proto = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO));

    /* Beware ref to dead object (we may be called from obj's finalizer). */
    return proto && proto->map ? proto : NULL;
}

JS_PUBLIC_API(JSBool)
JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto)
{
    CHECK_REQUEST(cx);
    OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto));
    return JS_TRUE;
}

JS_PUBLIC_API(JSObject *)
JS_GetParent(JSContext *cx, JSObject *obj)
{
    JSObject *parent;

    CHECK_REQUEST(cx);
    parent = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT));

    /* Beware ref to dead object (we may be called from obj's finalizer). */
    return parent && parent->map ? parent : NULL;
}

JS_PUBLIC_API(JSBool)
JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
{
    CHECK_REQUEST(cx);
    OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent));
    return JS_TRUE;
}

JS_PUBLIC_API(JSObject *)
JS_GetConstructor(JSContext *cx, JSObject *proto)
{
    JSBool ok;
    jsval cval;

    CHECK_REQUEST(cx);
    ok = OBJ_GET_PROPERTY(cx, proto,
			  (jsid)cx->runtime->atomState.constructorAtom,
			  &cval);
    if (!ok)
	return NULL;
    if (!JSVAL_IS_FUNCTION(cx, cval)) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR,
			     OBJ_GET_CLASS(cx, proto)->name);
	return NULL;
    }
    return JSVAL_TO_OBJECT(cval);
}

JS_PUBLIC_API(JSObject *)
JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
{
    CHECK_REQUEST(cx);
    return js_NewObject(cx, clasp, proto, parent);
}

JS_PUBLIC_API(JSObject *)
JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
		   JSObject *parent)
{
    CHECK_REQUEST(cx);
    return js_ConstructObject(cx, clasp, proto, parent);
}

static JSBool
DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
	       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
	       JSProperty **propp)
{
    jsid id;
    JSAtom *atom;

    if (attrs & JSPROP_INDEX) {
	id = INT_TO_JSVAL((jsint)name);
	atom = NULL;
    } else {
	atom = js_Atomize(cx, name, strlen(name), 0);
	if (!atom)
	    return JS_FALSE;
	id = (jsid)atom;
    }
    return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs,
			       propp);
}

#define AUTO_NAMELEN(s,n)   (((n) == (size_t)-1) ? js_strlen(s) : (n))

static JSBool
DefineUCProperty(JSContext *cx, JSObject *obj,
		 const jschar *name, size_t namelen, jsval value,
		 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
		 JSProperty **propp)
{
    JSAtom *atom;

    atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, value, getter, setter,
			       attrs, propp);
}

JS_PUBLIC_API(JSObject *)
JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,
		JSObject *proto, uintN attrs)
{
    JSObject *nobj;

    CHECK_REQUEST(cx);
    nobj = js_NewObject(cx, clasp, proto, obj);
    if (!nobj)
	return NULL;
    if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs,
			NULL)) {
	cx->newborn[GCX_OBJECT] = NULL;
	return NULL;
    }
    return nobj;
}

JS_PUBLIC_API(JSBool)
JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds)
{
    JSBool ok;
    jsval value;
    uintN flags;

    CHECK_REQUEST(cx);
    for (ok = JS_TRUE; cds->name; cds++) {
#if JS_ALIGN_OF_DOUBLE == 8
	/*
	 * The GC ignores references outside its pool such as &cds->dval,
	 * so we don't need to GC-alloc constant doubles.
	 */
	jsdouble d = cds->dval;
	jsint i;

	value = (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i))
		? INT_TO_JSVAL(i)
		: DOUBLE_TO_JSVAL(&cds->dval);
#else
	ok = js_NewNumberValue(cx, cds->dval, &value);
	if (!ok)
	    break;
#endif
	flags = cds->flags;
	if (!flags)
	    flags = JSPROP_READONLY | JSPROP_PERMANENT;
	ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, NULL);
	if (!ok)
	    break;
    }
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps)
{
    JSBool ok;
    JSProperty *prop;
    JSScopeProperty *sprop;

    CHECK_REQUEST(cx);
    for (ok = JS_TRUE; ps->name; ps++) {
	ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID,
			    ps->getter, ps->setter, ps->flags,
			    &prop);
	if (!ok)
	    break;
	if (prop) {
	    if (OBJ_IS_NATIVE(obj)) {
		sprop = (JSScopeProperty *)prop;
		sprop->id = INT_TO_JSVAL(ps->tinyid);
		sprop->attrs |= JSPROP_TINYIDHACK;
	    }
	    OBJ_DROP_PROPERTY(cx, obj, prop);
	}
    }
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
		  JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
{
    CHECK_REQUEST(cx);
    return DefineProperty(cx, obj, name, value, getter, setter, attrs, NULL);
}

JS_PUBLIC_API(JSBool)
JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name,
			    int8 tinyid, jsval value,
			    JSPropertyOp getter, JSPropertyOp setter,
			    uintN attrs)
{
    JSBool ok;
    JSProperty *prop;
    JSScopeProperty *sprop;

    CHECK_REQUEST(cx);
    ok = DefineProperty(cx, obj, name, value, getter, setter, attrs, &prop);
    if (ok && prop) {
	if (OBJ_IS_NATIVE(obj)) {
	    sprop = (JSScopeProperty *)prop;
	    sprop->id = INT_TO_JSVAL(tinyid);
	    sprop->attrs |= JSPROP_TINYIDHACK;
	}
	OBJ_DROP_PROPERTY(cx, obj, prop);
    }
    return ok;
}

static JSBool
LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp,
	       JSProperty **propp)
{
    JSAtom *atom;

    atom = js_Atomize(cx, name, strlen(name), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp);
}

static JSBool
LookupUCProperty(JSContext *cx, JSObject *obj,
		 const jschar *name, size_t namelen,
		 JSObject **objp, JSProperty **propp)
{
    JSAtom *atom;

    atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp);
}

JS_PUBLIC_API(JSBool)
JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name,
		 const char *alias)
{
    JSObject *obj2;
    JSProperty *prop;
    JSAtom *atom;
    JSScope *scope;
    JSBool ok;

    CHECK_REQUEST(cx);
    /* XXXbe push this into jsobj.c or jsscope.c */
    if (!LookupProperty(cx, obj, name, &obj2, &prop))
	return JS_FALSE;
    if (!prop) {
	js_ReportIsNotDefined(cx, name);
	return JS_FALSE;
    }
    if (obj2 != obj || !OBJ_IS_NATIVE(obj2)) {
	OBJ_DROP_PROPERTY(cx, obj2, prop);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS,
			     alias, name, OBJ_GET_CLASS(cx, obj2)->name);
	return JS_FALSE;
    }
    atom = js_Atomize(cx, alias, strlen(alias), 0);
    if (!atom) {
	ok = JS_FALSE;
    } else {
	scope = (JSScope *) obj->map;
	ok = (scope->ops->add(cx, scope, (jsid)atom, (JSScopeProperty *)prop)
	      != NULL);
    }
    OBJ_DROP_PROPERTY(cx, obj, prop);
    return ok;
}

static jsval
LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop)
{
    JSScopeProperty *sprop;
    jsval rval;

    if (!prop) {
	/* XXX bad API: no way to tell "not defined" from "void value" */
	return JSVAL_VOID;
    }
    if (OBJ_IS_NATIVE(obj2)) {
	/* Peek at the native property's slot value, without doing a Get. */
	sprop = (JSScopeProperty *)prop;
	rval = LOCKED_OBJ_GET_SLOT(obj2, sprop->slot);
    } else {
	/* XXX bad API: no way to return "defined but value unknown" */
	rval = JSVAL_TRUE;
    }
    OBJ_DROP_PROPERTY(cx, obj2, prop);
    return rval;
}

static JSBool
GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom,
		      uintN *attrsp, JSBool *foundp)
{
    JSObject *obj2;
    JSProperty *prop;
    JSBool ok;

    if (!atom)
	return JS_FALSE;
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
	return JS_FALSE;
    if (!prop || obj != obj2) {
	*foundp = JS_FALSE;
	if (prop)
	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	return JS_TRUE;
    }

    *foundp = JS_TRUE;
    ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, attrsp);
    OBJ_DROP_PROPERTY(cx, obj, prop);
    return ok;
}

static JSBool
SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom,
		      uintN attrs, JSBool *foundp)
{
    JSObject *obj2;
    JSProperty *prop;
    JSBool ok;

    if (!atom)
	return JS_FALSE;
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
	return JS_FALSE;
    if (!prop || obj != obj2) {
	*foundp = JS_FALSE;
	if (prop)
	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	return JS_TRUE;
    }

    *foundp = JS_TRUE;
    ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);
    OBJ_DROP_PROPERTY(cx, obj, prop);
    return ok;
}


JS_PUBLIC_API(JSBool)
JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
			 uintN *attrsp, JSBool *foundp)
{
    CHECK_REQUEST(cx);
    return GetPropertyAttributes(cx, obj,
				 js_Atomize(cx, name, strlen(name), 0),
				 attrsp, foundp);
}

JS_PUBLIC_API(JSBool)
JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
			 uintN attrs, JSBool *foundp)
{
    CHECK_REQUEST(cx);
    return SetPropertyAttributes(cx, obj,
				 js_Atomize(cx, name, strlen(name), 0),
				 attrs, foundp);
}

JS_PUBLIC_API(JSBool)
JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
{
    JSBool ok;
    JSObject *obj2;
    JSProperty *prop;

    CHECK_REQUEST(cx);
    ok = LookupProperty(cx, obj, name, &obj2, &prop);
    if (ok)
	*vp = LookupResult(cx, obj, obj2, prop);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_Atomize(cx, name, strlen(name), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp);
}

JS_PUBLIC_API(JSBool)
JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_Atomize(cx, name, strlen(name), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp);
}

JS_PUBLIC_API(JSBool)
JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name)
{
    jsval junk;

    CHECK_REQUEST(cx);
    return JS_DeleteProperty2(cx, obj, name, &junk);
}

JS_PUBLIC_API(JSBool)
JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name,
		   jsval *rval)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_Atomize(cx, name, strlen(name), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval);
}

JS_PUBLIC_API(JSBool)
JS_DefineUCProperty(JSContext *cx, JSObject *obj,
		    const jschar *name, size_t namelen, jsval value,
		    JSPropertyOp getter, JSPropertyOp setter,
		    uintN attrs)
{
    CHECK_REQUEST(cx);
    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter,
			    attrs, NULL);
}

JS_PUBLIC_API(JSBool)
JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj,
			   const jschar *name, size_t namelen,
			   uintN *attrsp, JSBool *foundp)
{
    CHECK_REQUEST(cx);
    return GetPropertyAttributes(cx, obj,
		    js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0),
		    attrsp, foundp);
}

JS_PUBLIC_API(JSBool)
JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj,
			   const jschar *name, size_t namelen,
			   uintN attrs, JSBool *foundp)
{
    CHECK_REQUEST(cx);
    return SetPropertyAttributes(cx, obj,
		    js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0),
		    attrs, foundp);
}

JS_PUBLIC_API(JSBool)
JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj,
			      const jschar *name, size_t namelen,
			      int8 tinyid, jsval value,
			      JSPropertyOp getter, JSPropertyOp setter,
			      uintN attrs)
{
    JSBool ok;
    JSProperty *prop;
    JSScopeProperty *sprop;

    CHECK_REQUEST(cx);
    ok = DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs,
			  &prop);
    if (ok && prop) {
	if (OBJ_IS_NATIVE(obj)) {
	    sprop = (JSScopeProperty *)prop;
	    sprop->id = INT_TO_JSVAL(tinyid);
	    sprop->attrs |= JSPROP_TINYIDHACK;
	}
	OBJ_DROP_PROPERTY(cx, obj, prop);
    }
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_LookupUCProperty(JSContext *cx, JSObject *obj,
		    const jschar *name, size_t namelen,
		    jsval *vp)
{
    JSBool ok;
    JSObject *obj2;
    JSProperty *prop;

    CHECK_REQUEST(cx);
    ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop);
    if (ok)
	*vp = LookupResult(cx, obj, obj2, prop);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_GetUCProperty(JSContext *cx, JSObject *obj,
		 const jschar *name, size_t namelen,
		 jsval *vp)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp);
}

JS_PUBLIC_API(JSBool)
JS_SetUCProperty(JSContext *cx, JSObject *obj,
		 const jschar *name, size_t namelen,
		 jsval *vp)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp);
}

JS_PUBLIC_API(JSBool)
JS_DeleteUCProperty2(JSContext *cx, JSObject *obj,
		     const jschar *name, size_t namelen,
		     jsval *rval)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0);
    if (!atom)
	return JS_FALSE;
    return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval);
}

JS_PUBLIC_API(JSObject *)
JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector)
{
    CHECK_REQUEST(cx);
    /* jsuint cast does ToUint32 */
    return js_NewArrayObject(cx, (jsuint)length, vector);
}

JS_PUBLIC_API(JSBool)
JS_IsArrayObject(JSContext *cx, JSObject *obj)
{
    CHECK_REQUEST(cx);
    return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass;
}

JS_PUBLIC_API(JSBool)
JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
    CHECK_REQUEST(cx);
    return js_GetLengthProperty(cx, obj, lengthp);
}

JS_PUBLIC_API(JSBool)
JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length)
{
    CHECK_REQUEST(cx);
    return js_SetLengthProperty(cx, obj, length);
}

JS_PUBLIC_API(JSBool)
JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
    CHECK_REQUEST(cx);
    return js_HasLengthProperty(cx, obj, lengthp);
}

JS_PUBLIC_API(JSBool)
JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value,
		 JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
{
    CHECK_REQUEST(cx);
    return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(index), value,
			       getter, setter, attrs, NULL);
}

JS_PUBLIC_API(JSBool)
JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias)
{
    JSObject *obj2;
    JSProperty *prop;
    JSScope *scope;
    JSBool ok;

    CHECK_REQUEST(cx);
    /* XXXbe push this into jsobj.c or jsscope.c */
    if (!LookupProperty(cx, obj, name, &obj2, &prop))
	return JS_FALSE;
    if (!prop) {
	js_ReportIsNotDefined(cx, name);
	return JS_FALSE;
    }
    if (obj2 != obj || !OBJ_IS_NATIVE(obj2)) {
	char numBuf[12];
	OBJ_DROP_PROPERTY(cx, obj2, prop);
	JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS,
			     numBuf, name, OBJ_GET_CLASS(cx, obj2)->name);
	return JS_FALSE;
    }
    scope = (JSScope *) obj->map;
    ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(alias),
			  (JSScopeProperty *)prop)
	  != NULL);
    OBJ_DROP_PROPERTY(cx, obj, prop);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
{
    JSBool ok;
    JSObject *obj2;
    JSProperty *prop;

    CHECK_REQUEST(cx);
    ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop);
    if (ok)
	*vp = LookupResult(cx, obj, obj2, prop);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
{
    CHECK_REQUEST(cx);
    return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp);
}

JS_PUBLIC_API(JSBool)
JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp)
{
    CHECK_REQUEST(cx);
    return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp);
}

JS_PUBLIC_API(JSBool)
JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index)
{
    jsval junk;

    CHECK_REQUEST(cx);
    return JS_DeleteElement2(cx, obj, index, &junk);
}

JS_PUBLIC_API(JSBool)
JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval)
{
    CHECK_REQUEST(cx);
    return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSVAL(index), rval);
}

JS_PUBLIC_API(void)
JS_ClearScope(JSContext *cx, JSObject *obj)
{
    JSObjectMap *map;
    JSScope *scope;

    CHECK_REQUEST(cx);
    /* XXXbe push this into jsobj.c or jsscope.c */
    JS_LOCK_OBJ(cx, obj);
    map = obj->map;
    if (MAP_IS_NATIVE(map)) {
	scope = (JSScope *)map;
	scope->ops->clear(cx, scope);
    }

    /* Reset freeslot so we're consistent. */
    map->freeslot = JSSLOT_FREE(OBJ_GET_CLASS(cx, obj));
    JS_UNLOCK_OBJ(cx, obj);
}

JS_PUBLIC_API(JSIdArray *)
JS_Enumerate(JSContext *cx, JSObject *obj)
{
    jsint i, n;
    jsval iter_state, num_properties;
    jsid id;
    JSIdArray *ida;
    jsval *vector;

    CHECK_REQUEST(cx);

    ida = NULL;
    iter_state = JSVAL_NULL;

    /* Get the number of properties to enumerate. */
    if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties))
	goto error;
    if (!JSVAL_IS_INT(num_properties)) {
	JS_ASSERT(0);
	goto error;
    }

    n = JSVAL_TO_INT(num_properties);

    /* Create an array of jsids large enough to hold all the properties */
    ida = js_NewIdArray(cx, n);
    if (n) {
	i = 0;
	vector = &ida->vector[0];
	while (1) {
	    if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id))
		goto error;

	    /* No more jsid's to enumerate ? */
	    if (iter_state == JSVAL_NULL)
		break;
	    vector[i++] = id;
	}
    }
/* BEGIN SRJ HACK FIX */
    else {
        if (iter_state != JSVAL_NULL)
	    OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
    }
/* END SRJ HACK FIX */

    return ida;

error:
    if (iter_state != JSVAL_NULL)
	OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
    if (ida)
	JS_DestroyIdArray(cx, ida);
    return NULL;
}

JS_PUBLIC_API(JSBool)
JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
	       jsval *vp, uintN *attrsp)
{
    CHECK_REQUEST(cx);
    return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp);
}

JS_PUBLIC_API(JSFunction *)
JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags,
	       JSObject *parent, const char *name)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);

    atom = NULL;
    if (name) {
	atom = js_Atomize(cx, name, strlen(name), 0);
	if (!atom)
	    return NULL;
    }
    return js_NewFunction(cx, NULL, call, nargs, flags, parent, atom);
}

JS_PUBLIC_API(JSObject *)
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
{
    JSFunction *fun;
    JSObject *newfunobj;

    CHECK_REQUEST(cx);
    if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) {
        /* Indicate we cannot clone this object */
	return funobj;
    }
    fun = JS_GetPrivate(cx, funobj);

    newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent);
    if (!newfunobj)
	return NULL;
    if (!js_LinkFunctionObject(cx, fun, newfunobj)) {
	cx->newborn[GCX_OBJECT] = NULL;
	return NULL;
    }

    return newfunobj;
}

JS_PUBLIC_API(JSObject *)
JS_GetFunctionObject(JSFunction *fun)
{
    return fun->object;
}

JS_PUBLIC_API(const char *)
JS_GetFunctionName(JSFunction *fun)
{
    return fun->atom
	   ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
	   : js_anonymous_str;
}

JS_PUBLIC_API(JSBool)
JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)
{
    JSFunction *fun;

    CHECK_REQUEST(cx);
    for (; fs->name; fs++) {
	fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs,
				fs->flags);
	if (!fun)
	    return JS_FALSE;
	fun->extra = fs->extra;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSFunction *)
JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call,
		  uintN nargs, uintN attrs)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_Atomize(cx, name, strlen(name), 0);
    if (!atom)
	return NULL;
    return js_DefineFunction(cx, obj, atom, call, nargs, attrs);
}

static JSScript *
CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts,
		   void *tempMark)
{
    JSCodeGenerator cg;
    JSScript *script;
    uintN lineno;

    CHECK_REQUEST(cx);
    if (!js_InitCodeGenerator(cx, &cg, ts->filename, ts->lineno,
			      ts->principals)) {
	script = NULL;
	goto out;
    }
    lineno = ts->lineno;
    if (!js_CompileTokenStream(cx, obj, ts, &cg)) {
	script = NULL;
	goto out;
    }
    script = js_NewScriptFromCG(cx, &cg, NULL);
out:
    if (!js_CloseTokenStream(cx, ts)) {
        if (script)
	    js_DestroyScript(cx, script);
	script = NULL;
    }
    cg.tempMark = tempMark;
    js_FinishCodeGenerator(cx, &cg);
    return script;
}

JS_PUBLIC_API(JSScript *)
JS_CompileScript(JSContext *cx, JSObject *obj,
		 const char *bytes, size_t length,
		 const char *filename, uintN lineno)
{
    jschar *chars;
    JSScript *script;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;
    script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno);
    JS_free(cx, chars);
    return script;
}

JS_PUBLIC_API(JSScript *)
JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj,
			      JSPrincipals *principals,
			      const char *bytes, size_t length,
			      const char *filename, uintN lineno)
{
    jschar *chars;
    JSScript *script;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;
    script = JS_CompileUCScriptForPrincipals(cx, obj, principals,
					     chars, length, filename, lineno);
    JS_free(cx, chars);
    return script;
}

JS_PUBLIC_API(JSScript *)
JS_CompileUCScript(JSContext *cx, JSObject *obj,
		   const jschar *chars, size_t length,
		   const char *filename, uintN lineno)
{
    CHECK_REQUEST(cx);
    return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length,
					   filename, lineno);
}

JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,
				JSPrincipals *principals,
				const jschar *chars, size_t length,
				const char *filename, uintN lineno)
{
    void *mark;
    JSTokenStream *ts;

    CHECK_REQUEST(cx);
    mark = JS_ARENA_MARK(&cx->tempPool);
    ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals);
    if (!ts)
	return NULL;
    return CompileTokenStream(cx, obj, ts, mark);
}

#ifdef JSFILE
JS_PUBLIC_API(JSScript *)
JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename)
{
    void *mark;
    JSTokenStream *ts;

    CHECK_REQUEST(cx);
    mark = JS_ARENA_MARK(&cx->tempPool);
    ts = js_NewFileTokenStream(cx, filename, stdin);
    if (!ts)
	return NULL;
    return CompileTokenStream(cx, obj, ts, mark);
}
#endif

JS_PUBLIC_API(JSObject *)
JS_NewScriptObject(JSContext *cx, JSScript *script)
{
    JSObject *obj;

    CHECK_REQUEST(cx);
    obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
    if (!obj)
	return NULL;
    if (script) {
	JS_SetPrivate(cx, obj, script);
	script->object = obj;
    }
    return obj;
}

JS_PUBLIC_API(void)
JS_DestroyScript(JSContext *cx, JSScript *script)
{
    CHECK_REQUEST(cx);
    js_DestroyScript(cx, script);
}

JS_PUBLIC_API(JSFunction *)
JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name,
		   uintN nargs, const char **argnames,
		   const char *bytes, size_t length,
		   const char *filename, uintN lineno)
{
    jschar *chars;
    JSFunction *fun;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;
    fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length,
			       filename, lineno);
    JS_free(cx, chars);
    return fun;
}

JS_PUBLIC_API(JSFunction *)
JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj,
				JSPrincipals *principals, const char *name,
				uintN nargs, const char **argnames,
				const char *bytes, size_t length,
				const char *filename, uintN lineno)
{
    jschar *chars;
    JSFunction *fun;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;
    fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name,
					    nargs, argnames, chars, length,
					    filename, lineno);
    JS_free(cx, chars);
    return fun;
}

JS_PUBLIC_API(JSFunction *)
JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name,
		     uintN nargs, const char **argnames,
		     const jschar *chars, size_t length,
		     const char *filename, uintN lineno)
{
    CHECK_REQUEST(cx);
    return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name,
					     nargs, argnames,
					     chars, length,
					     filename, lineno);
}

JS_PUBLIC_API(JSFunction *)
JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
				  JSPrincipals *principals, const char *name,
				  uintN nargs, const char **argnames,
				  const jschar *chars, size_t length,
				  const char *filename, uintN lineno)
{
    void *mark;
    JSTokenStream *ts;
    JSFunction *fun;
    JSAtom *funAtom, *argAtom;
    uintN i;
    JSScopeProperty *sprop;
    jsval junk;

    CHECK_REQUEST(cx);
    mark = JS_ARENA_MARK(&cx->tempPool);
    ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals);
    if (!ts) {
	fun = NULL;
	funAtom = NULL;
	goto out;
    }
    funAtom = js_Atomize(cx, name, strlen(name), 0);
    if (!funAtom) {
	fun = NULL;
	goto out;
    }
    fun = js_DefineFunction(cx, obj, funAtom, NULL, nargs, 0);
    if (!fun)
	goto out;
    if (nargs) {
	for (i = 0; i < nargs; i++) {
	    argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0);
	    if (!argAtom)
		break;
	    if (!js_DefineProperty(cx, fun->object, (jsid)argAtom,
				   JSVAL_VOID, js_GetArgument, js_SetArgument,
				   JSPROP_ENUMERATE|JSPROP_PERMANENT,
				   (JSProperty **)&sprop)) {
		break;
	    }
	    JS_ASSERT(sprop);
	    sprop->id = INT_TO_JSVAL(i);
	    OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop);
	}
	if (i < nargs) {
	    (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)funAtom, &junk);
	    fun = NULL;
	    goto out;
	}
    }
    if (!js_CompileFunctionBody(cx, ts, fun)) {
	(void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)funAtom, &junk);
	fun = NULL;
    }
out:
    if (ts)
	js_CloseTokenStream(cx, ts);
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return fun;
}

JS_PUBLIC_API(JSString *)
JS_DecompileScript(JSContext *cx, JSScript *script, const char *name,
		   uintN indent)
{
    JSPrinter *jp;
    JSString *str;

    CHECK_REQUEST(cx);
    jp = js_NewPrinter(cx, name, indent);
    if (!jp)
	return NULL;
    if (js_DecompileScript(jp, script))
	str = js_GetPrinterOutput(jp);
    else
	str = NULL;
    js_DestroyPrinter(jp);
    return str;
}

JS_PUBLIC_API(JSString *)
JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent)
{
    JSPrinter *jp;
    JSString *str;

    CHECK_REQUEST(cx);
    jp = js_NewPrinter(cx, JS_GetFunctionName(fun), indent);
    if (!jp)
	return NULL;
    if (js_DecompileFunction(jp, fun, JS_TRUE))
	str = js_GetPrinterOutput(jp);
    else
	str = NULL;
    js_DestroyPrinter(jp);
    return str;
}

JS_PUBLIC_API(JSString *)
JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent)
{
    JSPrinter *jp;
    JSString *str;

    CHECK_REQUEST(cx);
    jp = js_NewPrinter(cx, JS_GetFunctionName(fun), indent);
    if (!jp)
	return NULL;
    if (js_DecompileFunctionBody(jp, fun, JS_TRUE))
	str = js_GetPrinterOutput(jp);
    else
	str = NULL;
    js_DestroyPrinter(jp);
    return str;
}

JS_PUBLIC_API(JSBool)
JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval)
{
    CHECK_REQUEST(cx);
    if (!js_Execute(cx, obj, script, NULL, NULL, JS_FALSE, rval)) {
#if JS_HAS_EXCEPTIONS
        js_ReportUncaughtException(cx);
#endif
        return JS_FALSE;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_EvaluateScript(JSContext *cx, JSObject *obj,
		  const char *bytes, uintN length,
		  const char *filename, uintN lineno,
		  jsval *rval)
{
    jschar *chars;
    JSBool ok;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return JS_FALSE;
    ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval);
    JS_free(cx, chars);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj,
			       JSPrincipals *principals,
			       const char *bytes, uintN length,
			       const char *filename, uintN lineno,
			       jsval *rval)
{
    jschar *chars;
    JSBool ok;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return JS_FALSE;
    ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length,
					  filename, lineno, rval);
    JS_free(cx, chars);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_EvaluateUCScript(JSContext *cx, JSObject *obj,
		    const jschar *chars, uintN length,
		    const char *filename, uintN lineno,
		    jsval *rval)
{
    CHECK_REQUEST(cx);
    return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length,
					    filename, lineno, rval);
}

JS_PUBLIC_API(JSBool)
JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj,
				 JSPrincipals *principals,
				 const jschar *chars, uintN length,
				 const char *filename, uintN lineno,
				 jsval *rval)
{
    JSScript *script;
    JSBool ok;

    CHECK_REQUEST(cx);
    script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length,
					     filename, lineno);
    if (!script)
	return JS_FALSE;
    ok = js_Execute(cx, obj, script, NULL, NULL, JS_FALSE, rval);
#if JS_HAS_EXCEPTIONS
    if (!ok)
        js_ReportUncaughtException(cx);
#endif
    JS_DestroyScript(cx, script);
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc,
		jsval *argv, jsval *rval)
{
    CHECK_REQUEST(cx);
    if (!js_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(fun->object),
                              argc, argv, rval)) {
#if JS_HAS_EXCEPTIONS
        js_ReportUncaughtException(cx);
#endif
        return JS_FALSE;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc,
		    jsval *argv, jsval *rval)
{
    jsval fval;

    CHECK_REQUEST(cx);
    if (!JS_GetProperty(cx, obj, name, &fval))
	return JS_FALSE;
    if (!js_CallFunctionValue(cx, obj, fval, argc, argv, rval)) {
#if JS_HAS_EXCEPTIONS
        js_ReportUncaughtException(cx);
#endif
        return JS_FALSE;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
		     jsval *argv, jsval *rval)
{
    CHECK_REQUEST(cx);
    if (!js_CallFunctionValue(cx, obj, fval, argc, argv, rval)) {
#if JS_HAS_EXCEPTIONS
        js_ReportUncaughtException(cx);
#endif
        return JS_FALSE;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBranchCallback)
JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb)
{
    JSBranchCallback oldcb;

    CHECK_REQUEST(cx);
    oldcb = cx->branchCallback;
    cx->branchCallback = cb;
    return oldcb;
}

JS_PUBLIC_API(JSBool)
JS_IsRunning(JSContext *cx)
{
    return cx->fp != NULL;
}

JS_PUBLIC_API(JSBool)
JS_IsConstructing(JSContext *cx)
{
    CHECK_REQUEST(cx);
    return cx->fp && cx->fp->constructing;
}

/************************************************************************/

JS_PUBLIC_API(JSString *)
JS_NewString(JSContext *cx, char *bytes, size_t length)
{
    jschar *chars;
    JSString *str;

    CHECK_REQUEST(cx);
    /* Make a Unicode vector from the 8-bit char codes in bytes. */
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;

    /* Free chars (but not bytes, which caller frees on error) if we fail. */
    str = js_NewString(cx, chars, length, 0);
    if (!str) {
	JS_free(cx, chars);
	return NULL;
    }

    /* Hand off bytes to the deflated string cache, if possible. */
    if (!js_SetStringBytes(str, bytes, length))
	JS_free(cx, bytes);
    return str;
}

JS_PUBLIC_API(JSString *)
JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
{
    jschar *js;
    JSString *str;

    CHECK_REQUEST(cx);
    js = js_InflateString(cx, s, n);
    if (!js)
	return NULL;
    str = js_NewString(cx, js, n, 0);
    if (!str)
	JS_free(cx, js);
    return str;
}

JS_PUBLIC_API(JSString *)
JS_NewStringCopyZ(JSContext *cx, const char *s)
{
    size_t n;
    jschar *js;
    JSString *str;

    CHECK_REQUEST(cx);
    if (!s)
	return cx->runtime->emptyString;
    n = strlen(s);
    js = js_InflateString(cx, s, n);
    if (!js)
	return NULL;
    str = js_NewString(cx, js, n, 0);
    if (!str)
	JS_free(cx, js);
    return str;
}

JS_PUBLIC_API(JSString *)
JS_InternString(JSContext *cx, const char *s)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_Atomize(cx, s, strlen(s), ATOM_PINNED);
    if (!atom)
	return NULL;
    return ATOM_TO_STRING(atom);
}

JS_PUBLIC_API(JSString *)
JS_NewUCString(JSContext *cx, jschar *chars, size_t length)
{
    CHECK_REQUEST(cx);
    return js_NewString(cx, chars, length, 0);
}

JS_PUBLIC_API(JSString *)
JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n)
{
    CHECK_REQUEST(cx);
    return js_NewStringCopyN(cx, s, n, 0);
}

JS_PUBLIC_API(JSString *)
JS_NewUCStringCopyZ(JSContext *cx, const jschar *s)
{
    CHECK_REQUEST(cx);
    if (!s)
	return cx->runtime->emptyString;
    return js_NewStringCopyZ(cx, s, 0);
}

JS_PUBLIC_API(JSString *)
JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length)
{
    JSAtom *atom;

    CHECK_REQUEST(cx);
    atom = js_AtomizeChars(cx, s, length, ATOM_PINNED);
    if (!atom)
	return NULL;
    return ATOM_TO_STRING(atom);
}

JS_PUBLIC_API(JSString *)
JS_InternUCString(JSContext *cx, const jschar *s)
{
    return JS_InternUCStringN(cx, s, js_strlen(s));
}

JS_PUBLIC_API(char *)
JS_GetStringBytes(JSString *str)
{
    char *bytes = js_GetStringBytes(str);
    return bytes ? bytes : "";
}

JS_PUBLIC_API(jschar *)
JS_GetStringChars(JSString *str)
{
    return str->chars;
}

JS_PUBLIC_API(size_t)
JS_GetStringLength(JSString *str)
{
    return str->length;
}

JS_PUBLIC_API(intN)
JS_CompareStrings(JSString *str1, JSString *str2)
{
    return js_CompareStrings(str1, str2);
}

/************************************************************************/

JS_PUBLIC_API(void)
JS_ReportError(JSContext *cx, const char *format, ...)
{
    va_list ap;

    CHECK_REQUEST(cx);
    va_start(ap, format);
    js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap);
    va_end(ap);
}

JS_PUBLIC_API(void)
JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback,
		     void *userRef, const uintN errorNumber, ...)
{
    va_list ap;

    CHECK_REQUEST(cx);
    va_start(ap, errorNumber);
    js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
			   errorNumber, JS_TRUE, ap);
    va_end(ap);
}

JS_PUBLIC_API(void)
JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback,
		     void *userRef, const uintN errorNumber, ...)
{
    va_list ap;

    CHECK_REQUEST(cx);
    va_start(ap, errorNumber);
    js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef,
			   errorNumber, JS_FALSE, ap);
    va_end(ap);
}

JS_PUBLIC_API(void)
JS_ReportWarning(JSContext *cx, const char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap);
    va_end(ap);
}

JS_PUBLIC_API(void)
JS_ReportOutOfMemory(JSContext *cx)
{
    JS_ReportError(cx, "out of memory");
}

JS_PUBLIC_API(JSErrorReporter)
JS_SetErrorReporter(JSContext *cx, JSErrorReporter er)
{
    JSErrorReporter older;

    CHECK_REQUEST(cx);
    older = cx->errorReporter;
    cx->errorReporter = er;
    return older;
}

/************************************************************************/

/*
 * Regular Expressions.
 */
JS_PUBLIC_API(JSObject *)
JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags)
{
#if JS_HAS_REGEXPS
    jschar *chars;
    JSObject *obj;

    CHECK_REQUEST(cx);
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;
    obj = js_NewRegExpObject(cx, chars, length, flags);
    JS_free(cx, chars);
    return obj;
#else
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS);
    return NULL;
#endif
}

JS_PUBLIC_API(JSObject *)
JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags)
{
    CHECK_REQUEST(cx);
#if JS_HAS_REGEXPS
    return js_NewRegExpObject(cx, chars, length, flags);
#else
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS);
    return NULL;
#endif
}

JS_PUBLIC_API(void)
JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline)
{
    JSRegExpStatics *res;

    CHECK_REQUEST(cx);
    /* No locking required, cx is thread-private and input must be live. */
    res = &cx->regExpStatics;
    res->input = input;
    res->multiline = multiline;
}

JS_PUBLIC_API(void)
JS_ClearRegExpStatics(JSContext *cx)
{
    JSRegExpStatics *res;

    CHECK_REQUEST(cx);
    /* No locking required, cx is thread-private and input must be live. */
    res = &cx->regExpStatics;
    res->input = NULL;
    res->multiline = JS_FALSE;
    res->parenCount = 0;
    res->lastMatch = res->lastParen = js_EmptySubString;
    res->leftContext = res->rightContext = js_EmptySubString;
}

JS_PUBLIC_API(void)
JS_ClearRegExpRoots(JSContext *cx)
{
    JSRegExpStatics *res;

    CHECK_REQUEST(cx);
    /* No locking required, cx is thread-private and input must be live. */
    res = &cx->regExpStatics;
    res->input = NULL;
}

/* TODO: compile, execute, get/set other statics... */

/************************************************************************/

JS_PUBLIC_API(JSBool)
JS_IsExceptionPending(JSContext *cx)
{
    CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
    return (JSBool) cx->throwing;
#else
    return JS_FALSE;
#endif
}

JS_PUBLIC_API(JSBool)
JS_GetPendingException(JSContext *cx, jsval *vp)
{
    CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
    if (!cx->throwing)
	return JS_FALSE;
    *vp = cx->exception;
    return JS_TRUE;
#else
    return JS_FALSE;
#endif
}

JS_PUBLIC_API(void)
JS_SetPendingException(JSContext *cx, jsval v)
{
    CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
    cx->throwing = JS_TRUE;
    cx->exception = v;
#endif
}

JS_PUBLIC_API(void)
JS_ClearPendingException(JSContext *cx)
{
    CHECK_REQUEST(cx);
#if JS_HAS_EXCEPTIONS
    cx->throwing = JS_FALSE;
#endif
}

#if JS_HAS_EXCEPTIONS
struct JSExceptionState 
{
    JSBool throwing; 
    jsval  exception;
};
#endif

JS_PUBLIC_API(JSExceptionState *)
JS_SaveExceptionState(JSContext *cx)
{
#if JS_HAS_EXCEPTIONS
    JSExceptionState *state;
    CHECK_REQUEST(cx);
    state = (JSExceptionState*) JS_malloc(cx, sizeof(JSExceptionState));
    if (state) {
        state->throwing = JS_GetPendingException(cx, &state->exception);
        if (state->throwing && JSVAL_IS_GCTHING(state->exception))
            JS_AddRoot(cx, &state->exception);
    }
    return state;
#else
    return NULL;
#endif
}

JS_PUBLIC_API(void)
JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state)
{
#if JS_HAS_EXCEPTIONS
    CHECK_REQUEST(cx);
    if (state) {
        if (state->throwing)
            JS_SetPendingException(cx, state->exception);
        else
            JS_ClearPendingException(cx);
        JS_DropExceptionState(cx, state);
    }
#endif
}

JS_PUBLIC_API(void)
JS_DropExceptionState(JSContext *cx, JSExceptionState *state)
{
#if JS_HAS_EXCEPTIONS
    CHECK_REQUEST(cx);
    if (state) {
        if (state->throwing && JSVAL_IS_GCTHING(state->exception))
            JS_RemoveRoot(cx, &state->exception);
        JS_free(cx, state);
    }
#endif
}

#ifdef JS_THREADSAFE
JS_PUBLIC_API(intN)
JS_GetContextThread(JSContext *cx)
{
    return cx->thread;
}

JS_PUBLIC_API(intN)
JS_SetContextThread(JSContext *cx)
{
    intN old = cx->thread;
    cx->thread = js_CurrentThreadId();
    return old;
}

JS_PUBLIC_API(intN)
JS_ClearContextThread(JSContext *cx)
{
    intN old = cx->thread;
    cx->thread = 0;
    return old;
}
#endif

/************************************************************************/

JS_FRIEND_API(JSBool)
JS_IsAssigning(JSContext *cx)
{
    JSStackFrame *fp;
    jsbytecode *pc;

    if (!(fp = cx->fp) || !(pc = fp->pc))
	return JS_FALSE;
    return (js_CodeSpec[*pc].format & JOF_SET) != 0;
}

/************************************************************************/

#ifdef XP_PC
#include <windows.h>
#if defined(XP_OS2_HACK)
/*DSR031297 - the OS/2 equiv is dll_InitTerm, but I don't see the need for it*/
#else
/*
 * Initialization routine for the JS DLL...
 */

/*
 * Global Instance handle...
 * In Win32 this is the module handle of the DLL.
 *
 * In Win16 this is the instance handle of the application
 * which loaded the DLL.
 */

#ifdef _WIN32
BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
{
    return TRUE;
}

#else  /* !_WIN32 */

int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
		      WORD cbHeapSize, LPSTR lpszCmdLine )
{
    return TRUE;
}

BOOL CALLBACK __loadds WEP(BOOL fSystemExit)
{
    return TRUE;
}

#endif /* !_WIN32 */
#endif /* XP_OS2 */
#endif /* XP_PC */

**** End of jsapi.c. ****

**** Start of jsapi.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsapi_h___
#define jsapi_h___
/*
 * JavaScript API.
 */
#ifndef _STDDEF_H
	#include <stddef.h>
#endif
#ifndef jspubtd_h___
	#include "jspubtd.h"
#endif

JS_BEGIN_EXTERN_C

/*
 * Type tags stored in the low bits of a jsval.
 */
#define JSVAL_OBJECT            0x0     /* untagged reference to object */
#define JSVAL_INT               0x1     /* tagged 31-bit integer value */
#define JSVAL_DOUBLE            0x2     /* tagged reference to double */
#define JSVAL_STRING            0x4     /* tagged reference to string */
#define JSVAL_BOOLEAN           0x6     /* tagged boolean value */

/* Type tag bitfield length and derived macros. */
#define JSVAL_TAGBITS           3
#define JSVAL_TAGMASK           JS_BITMASK(JSVAL_TAGBITS)
#define JSVAL_TAG(v)            ((v) & JSVAL_TAGMASK)
#define JSVAL_SETTAG(v,t)       ((v) | (t))
#define JSVAL_CLRTAG(v)         ((v) & ~(jsval)JSVAL_TAGMASK)
#define JSVAL_ALIGN             JS_BIT(JSVAL_TAGBITS)

/* Predicates for type testing. */
#define JSVAL_IS_OBJECT(v)      (JSVAL_TAG(v) == JSVAL_OBJECT)
#define JSVAL_IS_NUMBER(v)      (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v))
#define JSVAL_IS_INT(v)         (((v) & JSVAL_INT) && (v) != JSVAL_VOID)
#define JSVAL_IS_DOUBLE(v)      (JSVAL_TAG(v) == JSVAL_DOUBLE)
#define JSVAL_IS_STRING(v)      (JSVAL_TAG(v) == JSVAL_STRING)
#define JSVAL_IS_BOOLEAN(v)     (JSVAL_TAG(v) == JSVAL_BOOLEAN)
#define JSVAL_IS_NULL(v)        ((v) == JSVAL_NULL)
#define JSVAL_IS_VOID(v)        ((v) == JSVAL_VOID)
#define JSVAL_IS_PRIMITIVE(v)   (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v))

/* Objects, strings, and doubles are GC'ed. */
#define JSVAL_IS_GCTHING(v)     (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v))
#define JSVAL_TO_GCTHING(v)     ((void *)JSVAL_CLRTAG(v))
#define JSVAL_TO_OBJECT(v)      ((JSObject *)JSVAL_TO_GCTHING(v))
#define JSVAL_TO_DOUBLE(v)      ((jsdouble *)JSVAL_TO_GCTHING(v))
#define JSVAL_TO_STRING(v)      ((JSString *)JSVAL_TO_GCTHING(v))
#define OBJECT_TO_JSVAL(obj)    ((jsval)(obj))
#define DOUBLE_TO_JSVAL(dp)     JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE)
#define STRING_TO_JSVAL(str)    JSVAL_SETTAG((jsval)(str), JSVAL_STRING)

/* Lock and unlock the GC thing held by a jsval. */
#define JSVAL_LOCK(cx,v)        (JSVAL_IS_GCTHING(v)                          \
				 ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v))    \
				 : JS_TRUE)
#define JSVAL_UNLOCK(cx,v)      (JSVAL_IS_GCTHING(v)                          \
				 ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v))  \
				 : JS_TRUE)

/* Domain limits for the jsval int type. */
#define JSVAL_INT_POW2(n)       ((jsval)1 << (n))
#define JSVAL_INT_MIN           ((jsval)1 - JSVAL_INT_POW2(30))
#define JSVAL_INT_MAX           (JSVAL_INT_POW2(30) - 1)
#define INT_FITS_IN_JSVAL(i)    ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX)
#define JSVAL_TO_INT(v)         ((jsint)(v) >> 1)
#define INT_TO_JSVAL(i)         (((jsval)(i) << 1) | JSVAL_INT)

/* Convert between boolean and jsval. */
#define JSVAL_TO_BOOLEAN(v)     ((JSBool)((v) >> JSVAL_TAGBITS))
#define BOOLEAN_TO_JSVAL(b)     JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS,     \
					     JSVAL_BOOLEAN)

/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */
#define JSVAL_TO_PRIVATE(v)     ((void *)((v) & ~JSVAL_INT))
#define PRIVATE_TO_JSVAL(p)     ((jsval)(p) | JSVAL_INT)

/* Property attributes, set in JSPropertySpec and passed to API functions. */
#define JSPROP_ENUMERATE        0x01    /* property is visible to for/in loop */
#define JSPROP_READONLY         0x02    /* not settable: assignment is no-op */
#define JSPROP_PERMANENT        0x04    /* property cannot be deleted */
#define JSPROP_EXPORTED         0x08    /* property is exported from object */
#define JSPROP_INDEX            0x20    /* name is actually (jsint) index */
#define JSPROP_ASSIGNHACK       0x40    /* property set by its assign method */
#define JSPROP_TINYIDHACK       0x80    /* prop->id is tinyid, not index */

/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */
#define JSFUN_BOUND_METHOD      0x40    /* bind this to fun->object's parent */
#define JSFUN_GLOBAL_PARENT     0x80    /* reparent calls to cx->globalObject */
#define JSFUN_FLAGS_MASK        0xc0    /* overlay JSPROP_*HACK attributes */

/*
 * Well-known JS values.  The extern'd variables are initialized when the
 * first JSContext is created by JS_NewContext (see below).
 */
#define JSVAL_VOID              INT_TO_JSVAL(0 - JSVAL_INT_POW2(30))
#define JSVAL_NULL              OBJECT_TO_JSVAL(0)
#define JSVAL_ZERO              INT_TO_JSVAL(0)
#define JSVAL_ONE               INT_TO_JSVAL(1)
#define JSVAL_FALSE             BOOLEAN_TO_JSVAL(JS_FALSE)
#define JSVAL_TRUE              BOOLEAN_TO_JSVAL(JS_TRUE)

/* Don't want to export data, so provide accessors for non-inline jsvals. */
extern JS_PUBLIC_API(jsval)
JS_GetNaNValue(JSContext *cx);

extern JS_PUBLIC_API(jsval)
JS_GetNegativeInfinityValue(JSContext *cx);

extern JS_PUBLIC_API(jsval)
JS_GetPositiveInfinityValue(JSContext *cx);

extern JS_PUBLIC_API(jsval)
JS_GetEmptyStringValue(JSContext *cx);

/*
 * Format is a string of the following characters (spaces are insignificant),
 * specifying the tabulated type conversions:
 *
 *   b      JSBool          Boolean
 *   c      uint16/jschar   ECMA uint16, Unicode char
 *   i      int32           ECMA int32
 *   u      uint32          ECMA uint32
 *   j      int32           Rounded int32 (coordinate)
 *   d      jsdouble        IEEE double
 *   I      jsdouble        Integral IEEE double
 *   s      char *          C string
 *   S      JSString *      Unicode string
 *   o      JSObject *      Object reference
 *   f      JSFunction *    Function private
 *   v      jsval           Argument value (no conversion)
 *   *      N/A             Skip this argument (no vararg)
 *   /      N/A             End of required arguments
 *
 * The variable argument list after format must consist of &b, &c, &s, e.g.,
 * where those variables have the types given above.  For the pointer types
 * char *, JSString *, and JSObject *, the pointed-at memory returned belongs
 * to the JS runtime, not to the calling native code.  The runtime promises
 * to keep this memory valid so long as argv refers to allocated stack space
 * (so long as the native function is active).
 *
 * Fewer arguments than format specifies may be passed only if there is a /
 * in format after the last required argument specifier and argc is at least
 * the number of required arguments.  More arguments than format specifies
 * may be passed without error; it is up to the caller to deal with trailing
 * unconverted arguments.
 */
extern JS_PUBLIC_API(JSBool)
JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format,
		    ...);

extern JS_PUBLIC_API(JSBool)
JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp);

extern JS_PUBLIC_API(JSFunction *)
JS_ValueToFunction(JSContext *cx, jsval v);

extern JS_PUBLIC_API(JSFunction *)
JS_ValueToConstructor(JSContext *cx, jsval v);

extern JS_PUBLIC_API(JSString *)
JS_ValueToString(JSContext *cx, jsval v);

extern JS_PUBLIC_API(JSBool)
JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp);

/*
 * Convert a value to a number, then to an int32, according to the ECMA rules
 * for ToInt32.
 */
extern JS_PUBLIC_API(JSBool)
JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip);

/*
 * Convert a value to a number, then to a uint32, according to the ECMA rules
 * for ToUint32.
 */
extern JS_PUBLIC_API(JSBool)
JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip);

/*
 * Convert a value to a number, then to an int32 if it fits by rounding to
 * nearest; but failing with an error report if the double is out of range
 * or unordered.
 */
extern JS_PUBLIC_API(JSBool)
JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip);

/*
 * ECMA ToUint16, for mapping a jsval to a Unicode point.
 */
extern JS_PUBLIC_API(JSBool)
JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip);

extern JS_PUBLIC_API(JSBool)
JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp);

extern JS_PUBLIC_API(JSType)
JS_TypeOfValue(JSContext *cx, jsval v);

extern JS_PUBLIC_API(const char *)
JS_GetTypeName(JSContext *cx, JSType type);

/************************************************************************/

/*
 * Initialization, locking, contexts, and memory allocation.
 */
#define JS_NewRuntime       JS_Init
#define JS_DestroyRuntime   JS_Finish
#define JS_LockRuntime      JS_Lock
#define JS_UnlockRuntime    JS_Unlock

extern JS_PUBLIC_API(JSRuntime *)
JS_NewRuntime(uint32 maxbytes);

extern JS_PUBLIC_API(void)
JS_DestroyRuntime(JSRuntime *rt);

extern JS_PUBLIC_API(void)
JS_ShutDown(void);

#ifdef JS_THREADSAFE

extern JS_PUBLIC_API(void)
JS_BeginRequest(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_EndRequest(JSContext *cx);

/* Yield to pending GC operations, regardless of request depth */
extern JS_PUBLIC_API(void)
JS_YieldRequest(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_SuspendRequest(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_ResumeRequest(JSContext *cx);

#endif /* JS_THREADSAFE */

extern JS_PUBLIC_API(void)
JS_Lock(JSRuntime *rt);

extern JS_PUBLIC_API(void)
JS_Unlock(JSRuntime *rt);

extern JS_PUBLIC_API(JSContext *)
JS_NewContext(JSRuntime *rt, size_t stacksize);

extern JS_PUBLIC_API(void)
JS_DestroyContext(JSContext *cx);

JS_EXTERN_API(void*)
JS_GetContextPrivate(JSContext *cx);

JS_EXTERN_API(void)
JS_SetContextPrivate(JSContext *cx, void *data);

extern JS_PUBLIC_API(JSRuntime *)
JS_GetRuntime(JSContext *cx);

extern JS_PUBLIC_API(JSContext *)
JS_ContextIterator(JSRuntime *rt, JSContext **iterp);

extern JS_PUBLIC_API(JSVersion)
JS_GetVersion(JSContext *cx);

extern JS_PUBLIC_API(JSVersion)
JS_SetVersion(JSContext *cx, JSVersion version);

extern JS_PUBLIC_API(const char *)
JS_GetImplementationVersion(void);

extern JS_PUBLIC_API(JSObject *)
JS_GetGlobalObject(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_SetGlobalObject(JSContext *cx, JSObject *obj);

/* NB: This sets cx's global object to obj if it was null. */
extern JS_PUBLIC_API(JSBool)
JS_InitStandardClasses(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSObject *)
JS_GetScopeChain(JSContext *cx);

extern JS_PUBLIC_API(void *)
JS_malloc(JSContext *cx, size_t nbytes);

extern JS_PUBLIC_API(void *)
JS_realloc(JSContext *cx, void *p, size_t nbytes);

extern JS_PUBLIC_API(void)
JS_free(JSContext *cx, void *p);

extern JS_PUBLIC_API(char *)
JS_strdup(JSContext *cx, const char *s);

extern JS_PUBLIC_API(jsdouble *)
JS_NewDouble(JSContext *cx, jsdouble d);

extern JS_PUBLIC_API(JSBool)
JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_AddRoot(JSContext *cx, void *rp);

extern JS_PUBLIC_API(JSBool)
JS_RemoveRoot(JSContext *cx, void *rp);

extern JS_PUBLIC_API(JSBool)
JS_AddNamedRoot(JSContext *cx, void *rp, const char *name);

#ifdef DEBUG
extern JS_PUBLIC_API(void)
JS_DumpNamedRoots(JSRuntime *rt,
		  void (*dump)(const char *name, void *rp, void *data),
		  void *data);
#endif

extern JS_PUBLIC_API(JSBool)
JS_LockGCThing(JSContext *cx, void *thing);

extern JS_PUBLIC_API(JSBool)
JS_UnlockGCThing(JSContext *cx, void *thing);

extern JS_PUBLIC_API(void)
JS_GC(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_MaybeGC(JSContext *cx);

extern JS_PUBLIC_API(JSGCCallback)
JS_SetGCCallback(JSContext *cx, JSGCCallback cb);

/************************************************************************/

/*
 * Classes, objects, and properties.
 */
struct JSClass {
    char                *name;
    uint32              flags;

    /* Mandatory non-null function pointer members. */
    JSPropertyOp        addProperty;
    JSPropertyOp        delProperty;
    JSPropertyOp        getProperty;
    JSPropertyOp        setProperty;
    JSEnumerateOp       enumerate;
    JSResolveOp         resolve;
    JSConvertOp         convert;
    JSFinalizeOp        finalize;

    /* Optionally non-null members start here. */
    JSGetObjectOps      getObjectOps;
    JSCheckAccessOp     checkAccess;
    JSNative            call;
    JSNative            construct;
    JSXDRObjectOp       xdrObject;
    JSHasInstanceOp     hasInstance;
    jsword              spare[2];
};

#define JSCLASS_HAS_PRIVATE     0x01    /* class instances have private slot */
#define JSCLASS_NEW_ENUMERATE   0x02    /* class has JSNewEnumerateOp method */
#define JSCLASS_NEW_RESOLVE     0x04    /* class has JSNewResolveOp method */
#define JSCLASS_FW_SIMPLE_GET   0x08    /* class does simple js_GetProperty */
#define JSCLASS_FW_ENUM		    0x10  

struct JSObjectOps {
    /* Mandatory non-null function pointer members. */
    JSNewObjectMapOp    newObjectMap;
    JSObjectMapOp       destroyObjectMap;
    JSLookupPropOp      lookupProperty;
    JSDefinePropOp      defineProperty;
    JSPropertyIdOp      getProperty;
    JSPropertyIdOp      setProperty;
    JSAttributesOp      getAttributes;
    JSAttributesOp      setAttributes;
    JSPropertyIdOp      deleteProperty;
    JSConvertOp         defaultValue;
    JSNewEnumerateOp    enumerate;
    JSCheckAccessIdOp   checkAccess;

    /* Optionally non-null members start here. */
    JSObjectOp          thisObject;
    JSPropertyRefOp     dropProperty;
    JSNative            call;
    JSNative            construct;
    JSXDRObjectOp       xdrObject;
    JSHasInstanceOp     hasInstance;
    jsword              spare[2];
};

/*
 * Classes that expose JSObjectOps via a non-null getObjectOps class hook may
 * derive a property structure from this struct, return a pointer to it from
 * lookupProperty and defineProperty, and use the pointer to avoid rehashing
 * in getAttributes and setAttributes.
 *
 * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an
 * internal pointer that is opaque to users of this API, but which users may
 * convert from and to a jsval using JS_ValueToId and JS_IdToValue.
 */
struct JSProperty {
    jsid id;
};

struct JSIdArray {
    jsint length;
    jsid  vector[1];    /* actually, length jsid words */
};

extern JS_PUBLIC_API(JSIdArray *)
JS_NewIdArray(JSContext *cx, jsint length);

extern JS_PUBLIC_API(void)
JS_DestroyIdArray(JSContext *cx, JSIdArray *ida);

extern JS_PUBLIC_API(JSBool)
JS_ValueToId(JSContext *cx, jsval v, jsid *idp);

extern JS_PUBLIC_API(JSBool)
JS_IdToValue(JSContext *cx, jsid id, jsval *vp);

#define JSRESOLVE_QUALIFIED     0x01    /* resolve a qualified property id */
#define JSRESOLVE_ASSIGNING     0x02    /* resolve on the left of assignment */

extern JS_PUBLIC_API(JSBool)
JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_EnumerateStub(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSBool)
JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id);

extern JS_PUBLIC_API(JSBool)
JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp);

extern JS_PUBLIC_API(void)
JS_FinalizeStub(JSContext *cx, JSObject *obj);

struct JSConstDoubleSpec {
    jsdouble        dval;
    const char      *name;
    uint8           flags;
    uint8           spare[3];
};

/*
 * To define an array element rather than a named property member, cast the
 * element's index to (const char *) and initialize name with it, and set the
 * JSPROP_INDEX bit in flags.
 */
struct JSPropertySpec {
    const char      *name;
    int8            tinyid;
    uint8           flags;
    JSPropertyOp    getter;
    JSPropertyOp    setter;
};

struct JSFunctionSpec {
    const char      *name;
    JSNative        call;
    uint8           nargs;
    uint8           flags;
    uint16          extra;
};

extern JS_PUBLIC_API(JSObject *)
JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
	     JSClass *clasp, JSNative constructor, uintN nargs,
	     JSPropertySpec *ps, JSFunctionSpec *fs,
	     JSPropertySpec *static_ps, JSFunctionSpec *static_fs);

#ifdef JS_THREADSAFE
extern JS_PUBLIC_API(JSClass *)
JS_GetClass(JSContext *cx, JSObject *obj);
#else
extern JS_PUBLIC_API(JSClass *)
JS_GetClass(JSObject *obj);
#endif

extern JS_PUBLIC_API(JSBool)
JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv);

extern JS_PUBLIC_API(void *)
JS_GetPrivate(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSBool)
JS_SetPrivate(JSContext *cx, JSObject *obj, void *data);

extern JS_PUBLIC_API(void *)
JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp,
		      jsval *argv);

extern JS_PUBLIC_API(JSObject *)
JS_GetPrototype(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSBool)
JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto);

extern JS_PUBLIC_API(JSObject *)
JS_GetParent(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSBool)
JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent);

extern JS_PUBLIC_API(JSObject *)
JS_GetConstructor(JSContext *cx, JSObject *proto);

extern JS_PUBLIC_API(JSObject *)
JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);

extern JS_PUBLIC_API(JSObject *)
JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
		   JSObject *parent);

extern JS_PUBLIC_API(JSObject *)
JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,
		JSObject *proto, uintN attrs);

extern JS_PUBLIC_API(JSBool)
JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds);

extern JS_PUBLIC_API(JSBool)
JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps);

extern JS_PUBLIC_API(JSBool)
JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,
		  JSPropertyOp getter, JSPropertyOp setter, uintN attrs);

/*
 * Determine the attributes (JSPROP_* flags) of a property on a given object.
 *
 * If the object does not have a property by that name, *foundp will be
 * JS_FALSE and the value of *attrsp is undefined.
 */
extern JS_PUBLIC_API(JSBool)
JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
			 uintN *attrsp, JSBool *foundp);

/*
 * Set the attributes of a property on a given object.
 *
 * If the object does not have a property by that name, *foundp will be
 * JS_FALSE and nothing will be altered.
 */
extern JS_PUBLIC_API(JSBool)
JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,
			 uintN attrs, JSBool *foundp);

extern JS_PUBLIC_API(JSBool)
JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name,
			    int8 tinyid, jsval value,
			    JSPropertyOp getter, JSPropertyOp setter,
			    uintN attrs);

extern JS_PUBLIC_API(JSBool)
JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name,
		 const char *alias);

extern JS_PUBLIC_API(JSBool)
JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name);

extern JS_PUBLIC_API(JSBool)
JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name,
		   jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_DefineUCProperty(JSContext *cx, JSObject *obj,
		    const jschar *name, size_t namelen, jsval value,
		    JSPropertyOp getter, JSPropertyOp setter,
		    uintN attrs);

/*
 * Determine the attributes (JSPROP_* flags) of a property on a given object.
 *
 * If the object does not have a property by that name, *foundp will be
 * JS_FALSE and the value of *attrsp is undefined.
 */
extern JS_PUBLIC_API(JSBool)
JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj,
			   const jschar *name, size_t namelen,
			   uintN *attrsp, JSBool *foundp);

/*
 * Set the attributes of a property on a given object.
 *
 * If the object does not have a property by that name, *foundp will be
 * JS_FALSE and nothing will be altered.
 */
extern JS_PUBLIC_API(JSBool)
JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj,
			   const jschar *name, size_t namelen,
			   uintN attrs, JSBool *foundp);


extern JS_PUBLIC_API(JSBool)
JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj,
			      const jschar *name, size_t namelen,
			      int8 tinyid, jsval value,
			      JSPropertyOp getter, JSPropertyOp setter,
			      uintN attrs);

extern JS_PUBLIC_API(JSBool)
JS_LookupUCProperty(JSContext *cx, JSObject *obj,
		    const jschar *name, size_t namelen,
		    jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_GetUCProperty(JSContext *cx, JSObject *obj,
		 const jschar *name, size_t namelen,
		 jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_SetUCProperty(JSContext *cx, JSObject *obj,
		 const jschar *name, size_t namelen,
		 jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_DeleteUCProperty2(JSContext *cx, JSObject *obj,
		     const jschar *name, size_t namelen,
		     jsval *rval);

extern JS_PUBLIC_API(JSObject *)
JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector);

extern JS_PUBLIC_API(JSBool)
JS_IsArrayObject(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSBool)
JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp);

extern JS_PUBLIC_API(JSBool)
JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length);

extern JS_PUBLIC_API(JSBool)
JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp);

extern JS_PUBLIC_API(JSBool)
JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value,
		 JSPropertyOp getter, JSPropertyOp setter, uintN attrs);

extern JS_PUBLIC_API(JSBool)
JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias);

extern JS_PUBLIC_API(JSBool)
JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp);

extern JS_PUBLIC_API(JSBool)
JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index);

extern JS_PUBLIC_API(JSBool)
JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval);

extern JS_PUBLIC_API(void)
JS_ClearScope(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSIdArray *)
JS_Enumerate(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(JSBool)
JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
	       jsval *vp, uintN *attrsp);

/************************************************************************/

/*
 * Security protocol.
 */
typedef struct JSPrincipals {
    char *codebase;
    void *(*getPrincipalArray)(JSContext *cx, struct JSPrincipals *);
    JSBool (*globalPrivilegesEnabled)(JSContext *cx, struct JSPrincipals *);

    /* Don't call "destroy"; use reference counting macros below. */
    uintN refcount;
    void (*destroy)(JSContext *cx, struct JSPrincipals *);
} JSPrincipals;

#define JSPRINCIPALS_HOLD(cx, principals)               \
    ((principals)->refcount++)
#define JSPRINCIPALS_DROP(cx, principals)               \
    ((--((principals)->refcount) == 0)                  \
	? (*(principals)->destroy)((cx), (principals))  \
	: (void) 0)

/************************************************************************/

/*
 * Functions and scripts.
 */
extern JS_PUBLIC_API(JSFunction *)
JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags,
	       JSObject *parent, const char *name);

extern JS_PUBLIC_API(JSObject *)
JS_GetFunctionObject(JSFunction *fun);

extern JS_PUBLIC_API(const char *)
JS_GetFunctionName(JSFunction *fun);

extern JS_PUBLIC_API(JSBool)
JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs);

extern JS_PUBLIC_API(JSFunction *)
JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call,
		  uintN nargs, uintN attrs);

extern JS_PUBLIC_API(JSObject *)
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);

extern JS_PUBLIC_API(JSScript *)
JS_CompileScript(JSContext *cx, JSObject *obj,
		 const char *bytes, size_t length,
		 const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSScript *)
JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj,
			      JSPrincipals *principals,
			      const char *bytes, size_t length,
			      const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSScript *)
JS_CompileUCScript(JSContext *cx, JSObject *obj,
		   const jschar *chars, size_t length,
		   const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,
				JSPrincipals *principals,
				const jschar *chars, size_t length,
				const char *filename, uintN lineno);

#ifdef JSFILE
extern JS_PUBLIC_API(JSScript *)
JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename);
#endif

extern JS_PUBLIC_API(JSObject *)
JS_NewScriptObject(JSContext *cx, JSScript *script);

extern JS_PUBLIC_API(void)
JS_DestroyScript(JSContext *cx, JSScript *script);

extern JS_PUBLIC_API(JSFunction *)
JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name,
		   uintN nargs, const char **argnames,
		   const char *bytes, size_t length,
		   const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSFunction *)
JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj,
				JSPrincipals *principals, const char *name,
				uintN nargs, const char **argnames,
				const char *bytes, size_t length,
				const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSFunction *)
JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name,
		     uintN nargs, const char **argnames,
		     const jschar *chars, size_t length,
		     const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSFunction *)
JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
				  JSPrincipals *principals, const char *name,
				  uintN nargs, const char **argnames,
				  const jschar *chars, size_t length,
				  const char *filename, uintN lineno);

extern JS_PUBLIC_API(JSString *)
JS_DecompileScript(JSContext *cx, JSScript *script, const char *name,
		   uintN indent);

extern JS_PUBLIC_API(JSString *)
JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent);

extern JS_PUBLIC_API(JSString *)
JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent);

extern JS_PUBLIC_API(JSBool)
JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_EvaluateScript(JSContext *cx, JSObject *obj,
		  const char *bytes, uintN length,
		  const char *filename, uintN lineno,
		  jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj,
			       JSPrincipals *principals,
			       const char *bytes, uintN length,
			       const char *filename, uintN lineno,
			       jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_EvaluateUCScript(JSContext *cx, JSObject *obj,
		    const jschar *chars, uintN length,
		    const char *filename, uintN lineno,
		    jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj,
				 JSPrincipals *principals,
				 const jschar *chars, uintN length,
				 const char *filename, uintN lineno,
				 jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc,
		jsval *argv, jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc,
		    jsval *argv, jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,
		     jsval *argv, jsval *rval);

extern JS_PUBLIC_API(JSBranchCallback)
JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb);

extern JS_PUBLIC_API(JSBool)
JS_IsRunning(JSContext *cx);

extern JS_PUBLIC_API(JSBool)
JS_IsConstructing(JSContext *cx);

/************************************************************************/

/*
 * Strings.
 */
extern JS_PUBLIC_API(JSString *)
JS_NewString(JSContext *cx, char *bytes, size_t length);

extern JS_PUBLIC_API(JSString *)
JS_NewStringCopyN(JSContext *cx, const char *s, size_t n);

extern JS_PUBLIC_API(JSString *)
JS_NewStringCopyZ(JSContext *cx, const char *s);

extern JS_PUBLIC_API(JSString *)
JS_InternString(JSContext *cx, const char *s);

extern JS_PUBLIC_API(JSString *)
JS_NewUCString(JSContext *cx, jschar *chars, size_t length);

extern JS_PUBLIC_API(JSString *)
JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n);

extern JS_PUBLIC_API(JSString *)
JS_NewUCStringCopyZ(JSContext *cx, const jschar *s);

extern JS_PUBLIC_API(JSString *)
JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length);

extern JS_PUBLIC_API(JSString *)
JS_InternUCString(JSContext *cx, const jschar *s);

extern JS_PUBLIC_API(char *)
JS_GetStringBytes(JSString *str);

extern JS_PUBLIC_API(jschar *)
JS_GetStringChars(JSString *str);

extern JS_PUBLIC_API(size_t)
JS_GetStringLength(JSString *str);

extern JS_PUBLIC_API(intN)
JS_CompareStrings(JSString *str1, JSString *str2);

#if XP_MAC
/************************************************************************/

/*
 * Fireworks specific
 */
typedef JSBool (*EnumDestroyerProcPtr)(jsval iter_state);

extern JS_PUBLIC_API(void) 
js_SetFwEnumDestroyer(EnumDestroyerProcPtr destroyer);

#endif

/************************************************************************/

/*
 * Error reporting.
 */

/*
 * Report an exception represented by the sprintf-like conversion of format
 * and its arguments.  This exception message string is passed to a pre-set
 * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for
 * the JSErrorReporter typedef).
 */
extern JS_PUBLIC_API(void)
JS_ReportError(JSContext *cx, const char *format, ...);

/*
 * Use an errorNumber to retrieve the format string, args are char *
 */
extern JS_PUBLIC_API(void)
JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback,
		     void *userRef, const uintN errorNumber, ...);

/*
 * Use an errorNumber to retrieve the format string, args are jschar *
 */
extern JS_PUBLIC_API(void)
JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback,
		     void *userRef, const uintN errorNumber, ...);

/*
 * As above, but report a warning instead (JSREPORT_IS_WARNING(report->flags)).
 */
extern JS_PUBLIC_API(void)
JS_ReportWarning(JSContext *cx, const char *format, ...);

/*
 * Complain when out of memory.
 */
extern JS_PUBLIC_API(void)
JS_ReportOutOfMemory(JSContext *cx);

struct JSErrorReport {
    const char      *filename;      /* source file name, URL, etc., or null */
    uintN           lineno;         /* source line number */
    const char      *linebuf;       /* offending source line without final \n */
    const char      *tokenptr;      /* pointer to error token in linebuf */
    const jschar    *uclinebuf;     /* unicode (original) line buffer */
    const jschar    *uctokenptr;    /* unicode (original) token pointer */
    uintN	    flags;          /* error/warning, etc. */
    uintN           errorNumber;    /* the error number, e.g. see js.msg */
    const jschar    *ucmessage;     /* the (default) error message */
    const jschar    **messageArgs;  /* arguments for the error message */
};

/*
 * JSErrorReport flag values.
 */

/* XXX need better classification system */
#define JSREPORT_ERROR			0x0
#define JSREPORT_WARNING		0x1 /* reported via JS_ReportWarning */

/*
 * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception
 * has been thrown for this runtime error, and the host should ignore it.
 * Exception-aware hosts should also check for JS_IsExceptionPending if
 * JS_ExecuteScript returns failure, and signal or propagate the exception, as
 * appropriate.
 */
#define JSREPORT_EXCEPTION		0x2

#define JSREPORT_IS_WARNING(flags)	(flags & JSREPORT_WARNING)
#define JSREPORT_IS_EXCEPTION(flags)	(flags & JSREPORT_EXCEPTION)

extern JS_PUBLIC_API(JSErrorReporter)
JS_SetErrorReporter(JSContext *cx, JSErrorReporter er);

/************************************************************************/

/*
 * Regular Expressions.
 */
#define JSREG_FOLD      0x01    /* fold uppercase to lowercase */
#define JSREG_GLOB      0x02    /* global exec, creates array of matches */

extern JS_PUBLIC_API(JSObject *)
JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags);

extern JS_PUBLIC_API(JSObject *)
JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags);

extern JS_PUBLIC_API(void)
JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline);

extern JS_PUBLIC_API(void)
JS_ClearRegExpStatics(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_ClearRegExpRoots(JSContext *cx);

/* TODO: compile, exec, get/set other statics... */

/************************************************************************/

extern JS_PUBLIC_API(JSBool)
JS_IsExceptionPending(JSContext *cx);

extern JS_PUBLIC_API(JSBool)
JS_GetPendingException(JSContext *cx, jsval *vp);

extern JS_PUBLIC_API(void)
JS_SetPendingException(JSContext *cx, jsval v);

extern JS_PUBLIC_API(void)
JS_ClearPendingException(JSContext *cx);

/*                                                                              
* Save the current exception state. This takes a snapshot of the current        
* exception state without making any change to that state.                      
*                                                                               
* The returned object MUST be later passed to either JS_RestoreExceptionState   
* (to restore that saved state) or JS_DropExceptionState (to cleanup the state  
* object in case it is not desireable to restore to that state). Both           
* JS_RestoreExceptionState and JS_DropExceptionState will destroy the          
* JSExceptionState object -- so that object can not be referenced again 
* after making either of those calls.                                                        
*/                                                                              

extern JS_PUBLIC_API(JSExceptionState *)
JS_SaveExceptionState(JSContext *cx);

extern JS_PUBLIC_API(void)
JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state);

extern JS_PUBLIC_API(void)
JS_DropExceptionState(JSContext *cx, JSExceptionState *state);

#ifdef JS_THREADSAFE
/*
 * Associate the current thread with the given context.  This is done
 * implicitly by JS_NewContext.
 *
 * Returns the old thread id for this context, which should be treated as
 * an opaque value.  This value is provided for comparison to 0, which
 * indicates that ClearContextThread has been called on this context
 * since the last SetContextThread, or non-0, which indicates the opposite.
 */

extern JS_PUBLIC_API(intN)
JS_GetContextThread(JSContext *cx);

extern JS_PUBLIC_API(intN)
JS_SetContextThread(JSContext *cx);

extern JS_PUBLIC_API(intN)
JS_ClearContextThread(JSContext *cx);
#endif

/************************************************************************/

/*
 * Returns true if a script is executing and its current bytecode is a set
 * (assignment) operation.
 *
 * NOTE: Previously conditional on NETSCAPE_INTERNAL. This function may 
 * be removed in the future.
 */
extern JS_FRIEND_API(JSBool)
JS_IsAssigning(JSContext *cx);

JS_END_EXTERN_C

#endif /* jsapi_h___ */

**** End of jsapi.h. ****

**** Start of jsarena.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * Lifetime-based fast allocation, inspired by much prior art, including
 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsbit.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */

#ifdef JS_THREADSAFE
extern js_CompareAndSwap(jsword *, jsword, jsword);
#endif

static JSArena *arena_freelist;

#ifdef JS_ARENAMETER
static JSArenaStats *arena_stats_list;

#define COUNT(pool,what)  (pool)->stats.what++
#else
#define COUNT(pool,what)  /* nothing */
#endif

#define JS_ARENA_DEFAULT_ALIGN  sizeof(double)

JS_EXPORT_API(void)
JS_InitArenaPool(JSArenaPool *pool, const char *name, JSUint32 size, JSUint32 align)
{
    if (align == 0)
	align = JS_ARENA_DEFAULT_ALIGN;
    pool->mask = JS_BITMASK(JS_CeilingLog2(align));
    pool->first.next = NULL;
    pool->first.base = pool->first.avail = pool->first.limit =
	(jsuword)JS_ARENA_ALIGN(pool, &pool->first + 1);
    pool->current = &pool->first;
    pool->arenasize = size;
#ifdef JS_ARENAMETER
    memset(&pool->stats, 0, sizeof pool->stats);
    pool->stats.name = strdup(name);
    pool->stats.next = arena_stats_list;
    arena_stats_list = &pool->stats;
#endif
}

JS_EXPORT_API(void *)
JS_ArenaAllocate(JSArenaPool *pool, JSUint32 nb)
{
    JSArena **ap, *a, *b;
#ifdef JS_THREADSAFE
    JSArena *c;
#endif
    JSUint32 sz;
    void *p;

    JS_ASSERT((nb & pool->mask) == 0);
#if defined(XP_PC) && !defined(_WIN32)
    if (nb >= 60000U)
	return 0;
#endif  /* WIN16 */
    ap = &arena_freelist;
    for (a = pool->current; a->avail + nb > a->limit; pool->current = a) {
        if (a->next) {                          /* move to next arena */
            a = a->next;
	    continue;
        }
	while ((b = *ap) != NULL) {		/* reclaim a free arena */
	    if (b->limit - b->base == pool->arenasize) {
#ifdef JS_THREADSAFE
		do {
                    b = *ap;
		    c = b->next;
		} while (!js_CompareAndSwap((jsword *)ap,(jsword)b,(jsword)c));
#else
		*ap = b->next;
#endif
		b->next = NULL;
		a = a->next = b;
		COUNT(pool, nreclaims);
		goto claim;
	    }
	    ap = &b->next;
	}
	sz = JS_MAX(pool->arenasize, nb);	/* allocate a new arena */
	sz += sizeof *a + pool->mask;           /* header and alignment slop */
	b = malloc(sz);
	if (!b)
	    return 0;
	a = a->next = b;
	a->next = NULL;
	a->limit = (jsuword)a + sz;
	JS_COUNT_ARENA(pool,++);
	COUNT(pool, nmallocs);
    claim:
	a->base = a->avail = (jsuword)JS_ARENA_ALIGN(pool, a + 1);
    }
    p = (void *)a->avail;
    a->avail += nb;
    return p;
}

JS_EXPORT_API(void *)
JS_ArenaGrow(JSArenaPool *pool, void *p, JSUint32 size, JSUint32 incr)
{
    void *newp;

    JS_ARENA_ALLOCATE(newp, pool, size + incr);
    // 001011 srj : must check for null result!
    if (newp != 0)
	    memcpy(newp, p, size);
    return newp;
}

/*
 * Free tail arenas linked after head, which may not be the true list head.
 * Reset pool->current to point to head in case it pointed at a tail arena.
 */
static void
FreeArenaList(JSArenaPool *pool, JSArena *head, JSBool reallyFree)
{
    JSArena **ap, *a;
#ifdef JS_THREADSAFE
    JSArena *b;
#endif

    ap = &head->next;
    a = *ap;
    if (!a)
	return;

#ifdef DEBUG
    do {
	JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
	a->avail = a->base;
	JS_CLEAR_UNUSED(a);
    } while ((a = a->next) != NULL);
    a = *ap;
#endif

    if (reallyFree) {
	do {
	    *ap = a->next;
	    JS_CLEAR_ARENA(a);
	    JS_COUNT_ARENA(pool,--);
	    free(a);
	} while ((a = *ap) != NULL);
    } else {
	/* Insert the whole arena chain at the front of the freelist. */
	do {
	    ap = &(*ap)->next;
	} while (*ap);
#ifdef JS_THREADSAFE
	do {
	    *ap = b = arena_freelist;
	} while (!js_CompareAndSwap((jsword*)&arena_freelist,(jsword)b,(jsword)a));
#else
	*ap = arena_freelist;
	arena_freelist = a;
#endif
	head->next = NULL;
    }

    pool->current = head;
}

JS_EXPORT_API(void)
JS_ArenaRelease(JSArenaPool *pool, char *mark)
{
    JSArena *a;

    for (a = pool->first.next; a; a = a->next) {
	if (JS_UPTRDIFF(mark, a) < JS_UPTRDIFF(a->avail, a)) {
	    a->avail = (jsuword)JS_ARENA_ALIGN(pool, mark);
	    FreeArenaList(pool, a, JS_TRUE);
	    return;
	}
    }
}

JS_EXPORT_API(void)
JS_FreeArenaPool(JSArenaPool *pool)
{
    FreeArenaList(pool, &pool->first, JS_FALSE);
    COUNT(pool, ndeallocs);
}

JS_EXPORT_API(void)
JS_FinishArenaPool(JSArenaPool *pool)
{
    FreeArenaList(pool, &pool->first, JS_TRUE);
#ifdef JS_ARENAMETER
    {
	JSArenaStats *stats, **statsp;

	if (pool->stats.name)
	    free(pool->stats.name);
	for (statsp = &arena_stats_list; (stats = *statsp) != 0;
	     statsp = &stats->next) {
	    if (stats == &pool->stats) {
		*statsp = stats->next;
		return;
	    }
	}
    }
#endif
}

JS_EXPORT_API(void)
JS_CompactArenaPool(JSArenaPool *pool)
{
#if 0 /* XP_MAC */
    JSArena *a = pool->first.next;

    while (a) {
        reallocSmaller(a, a->avail - (jsuword)a);
        a->limit = a->avail;
        a = a->next;
    }
#endif
}

JS_EXPORT_API(void)
JS_ArenaFinish()
{
    JSArena *a, *next;

#ifdef JS_THREADSAFE
    while (arena_freelist) {
	a = arena_freelist;
	next = a->next;
	if (js_CompareAndSwap((jsword*)&arena_freelist,(jsword)a,(jsword)next))
	    free(a);
    }
#else
    for (a = arena_freelist; a; a = next) {
        next = a->next;
        free(a);
    }
    arena_freelist = NULL;
#endif
}

#ifdef JS_ARENAMETER
JS_EXPORT_API(void)
JS_ArenaCountAllocation(JSArenaPool *pool, JSUint32 nb)
{
    pool->stats.nallocs++;
    pool->stats.nbytes += nb;
    if (nb > pool->stats.maxalloc)
        pool->stats.maxalloc = nb;
    pool->stats.variance += nb * nb;
}

JS_EXPORT_API(void)
JS_ArenaCountInplaceGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr)
{
    pool->stats.ninplace++;
}

JS_EXPORT_API(void)
JS_ArenaCountGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr)
{
    pool->stats.ngrows++;
    pool->stats.nbytes += incr;
    pool->stats.variance -= size * size;
    size += incr;
    if (size > pool->stats.maxalloc)
        pool->stats.maxalloc = size;
    pool->stats.variance += size * size;
}

JS_EXPORT_API(void)
JS_ArenaCountRelease(JSArenaPool *pool, char *mark)
{
    pool->stats.nreleases++;
}

JS_EXPORT_API(void)
JS_ArenaCountRetract(JSArenaPool *pool, char *mark)
{
    pool->stats.nfastrels++;
}

#include <math.h>
#include <stdio.h>

JS_EXPORT_API(void)
JS_DumpArenaStats(FILE *fp)
{
    JSArenaStats *stats;
    double mean, variance;

    for (stats = arena_stats_list; stats; stats = stats->next) {
        if (stats->nallocs != 0) {
	    mean = (double)stats->nbytes / stats->nallocs;
	    variance = fabs(stats->variance / stats->nallocs - mean * mean);
	} else {
	    mean = variance = 0;
	}

        fprintf(fp, "\n%s allocation statistics:\n", stats->name);
        fprintf(fp, "              number of arenas: %u\n", stats->narenas);
        fprintf(fp, "         number of allocations: %u\n", stats->nallocs);
        fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
        fprintf(fp, "        number of malloc calls: %u\n", stats->nmallocs);
        fprintf(fp, "       number of deallocations: %u\n", stats->ndeallocs);
        fprintf(fp, "  number of allocation growths: %u\n", stats->ngrows);
        fprintf(fp, "    number of in-place growths: %u\n", stats->ninplace);
        fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
        fprintf(fp, "       number of fast releases: %u\n", stats->nfastrels);
        fprintf(fp, "         total bytes allocated: %u\n", stats->nbytes);
        fprintf(fp, "          mean allocation size: %g\n", mean);
        fprintf(fp, "            standard deviation: %g\n", sqrt(variance));
        fprintf(fp, "       maximum allocation size: %u\n", stats->maxalloc);
    }
}
#endif /* JS_ARENAMETER */

**** End of jsarena.c. ****

**** Start of jsarena.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsarena_h___
#define jsarena_h___
/*
 * Lifetime-based fast allocation, inspired by much prior art, including
 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
 *
 * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE).
 */
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jscompat.h"

JS_BEGIN_EXTERN_C

typedef struct JSArena JSArena;
typedef struct JSArenaPool JSArenaPool;

struct JSArena {
    JSArena     *next;          /* next arena for this lifetime */
    jsuword     base;           /* aligned base address, follows this header */
    jsuword     limit;          /* one beyond last byte in arena */
    jsuword     avail;          /* points to next available byte */
};

#ifdef JS_ARENAMETER
typedef struct JSArenaStats JSArenaStats;

struct JSArenaStats {
    JSArenaStats *next;         /* next in arenaStats list */
    char        *name;          /* name for debugging */
    uint32      narenas;        /* number of arenas in pool */
    uint32      nallocs;        /* number of JS_ARENA_ALLOCATE() calls */
    uint32      nreclaims;      /* number of reclaims from freeArenas */
    uint32      nmallocs;       /* number of malloc() calls */
    uint32      ndeallocs;      /* number of lifetime deallocations */
    uint32      ngrows;         /* number of JS_ARENA_GROW() calls */
    uint32      ninplace;       /* number of in-place growths */
    uint32      nreleases;      /* number of JS_ARENA_RELEASE() calls */
    uint32      nfastrels;      /* number of "fast path" releases */
    size_t      nbytes;         /* total bytes allocated */
    size_t      maxalloc;       /* maximum allocation size in bytes */
    double      variance;       /* size variance accumulator */
};
#endif

struct JSArenaPool {
    JSArena     first;          /* first arena in pool list */
    JSArena     *current;       /* arena from which to allocate space */
    size_t      arenasize;      /* net exact size of a new arena */
    jsuword     mask;           /* alignment mask (power-of-2 - 1) */
#ifdef JS_ARENAMETER
    JSArenaStats stats;
#endif
};

/*
 * If the including .c file uses only one power-of-2 alignment, it may define
 * JS_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions
 * per ALLOCATE and GROW.
 */
#ifdef JS_ARENA_CONST_ALIGN_MASK
#define JS_ARENA_ALIGN(pool, n)	(((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \
				 & ~JS_ARENA_CONST_ALIGN_MASK)

#define JS_INIT_ARENA_POOL(pool, name, size) \
	JS_InitArenaPool(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1)
#else
#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask)
#endif

#define JS_ARENA_ALLOCATE(p, pool, nb)                                        \
    JS_BEGIN_MACRO                                                            \
	JSArena *_a = (pool)->current;                                        \
	size_t _nb = JS_ARENA_ALIGN(pool, nb);                                \
	jsuword _p = _a->avail;                                               \
	jsuword _q = _p + _nb;                                                \
	if (_q > _a->limit)                                                   \
	    _p = (jsuword)JS_ArenaAllocate(pool, _nb);                        \
	else                                                                  \
	    _a->avail = _q;                                                   \
	p = (void *)_p;                                                       \
	JS_ArenaCountAllocation(pool, nb);                                    \
    JS_END_MACRO

#define JS_ARENA_GROW(p, pool, size, incr)                                    \
    JS_BEGIN_MACRO                                                            \
	JSArena *_a = (pool)->current;                                        \
	size_t _incr = JS_ARENA_ALIGN(pool, incr);                            \
	jsuword _p = _a->avail;                                               \
	jsuword _q = _p + _incr;                                              \
	if (_p == (jsuword)(p) + JS_ARENA_ALIGN(pool, size) &&                \
	    _q <= _a->limit) {                                                \
	    _a->avail = _q;                                                   \
	    JS_ArenaCountInplaceGrowth(pool, size, incr);                     \
	} else {                                                              \
	    p = JS_ArenaGrow(pool, p, size, incr);                            \
	}                                                                     \
	JS_ArenaCountGrowth(pool, size, incr);                                \
    JS_END_MACRO

#define JS_ARENA_MARK(pool)     ((void *) (pool)->current->avail)
#define JS_UPTRDIFF(p,q)	((jsuword)(p) - (jsuword)(q))

#ifdef DEBUG
#define free_PATTERN         0xDA
#define JS_CLEAR_UNUSED(a)	(JS_ASSERT((a)->avail <= (a)->limit),         \
				 memset((void*)(a)->avail, free_PATTERN,   \
					(a)->limit - (a)->avail))
#define JS_CLEAR_ARENA(a)       memset((void*)(a), free_PATTERN,           \
				       (a)->limit - (jsuword)(a))
#else
#define JS_CLEAR_UNUSED(a)	/* nothing */
#define JS_CLEAR_ARENA(a)       /* nothing */
#endif

#define JS_ARENA_RELEASE(pool, mark)                                          \
    JS_BEGIN_MACRO                                                            \
	char *_m = (char *)(mark);                                            \
	JSArena *_a = (pool)->current;                                        \
	if (JS_UPTRDIFF(_m, _a) <= JS_UPTRDIFF(_a->avail, _a)) {              \
	    _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m);                    \
	    JS_CLEAR_UNUSED(_a);                                              \
	    JS_ArenaCountRetract(pool, _m);                                   \
	} else {                                                              \
	    JS_ArenaRelease(pool, _m);                                        \
	}                                                                     \
	JS_ArenaCountRelease(pool, _m);                                       \
    JS_END_MACRO

#ifdef JS_ARENAMETER
#define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op)
#else
#define JS_COUNT_ARENA(pool,op)
#endif

#define JS_ARENA_DESTROY(pool, a, pnext)                                      \
    JS_BEGIN_MACRO                                                            \
	JS_COUNT_ARENA(pool,--);                                              \
	if ((pool)->current == (a)) (pool)->current = &(pool)->first;         \
	*(pnext) = (a)->next;                                                 \
	JS_CLEAR_ARENA(a);                                                    \
	free(a);                                                              \
	(a) = NULL;                                                           \
    JS_END_MACRO

/*
 * Initialize an arena pool with the given name for debugging and metering,
 * with a minimum size per arena of size bytes.
 */
JS_EXTERN_API(void)
JS_InitArenaPool(JSArenaPool *pool, const char *name, JSUint32 size,
		 JSUint32 align);

/*
 * Free the arenas in pool.  The user may continue to allocate from pool
 * after calling this function.  There is no need to call JS_InitArenaPool()
 * again unless JS_FinishArenaPool(pool) has been called.
 */
JS_EXTERN_API(void)
JS_FreeArenaPool(JSArenaPool *pool);

/*
 * Free the arenas in pool and finish using it altogether.
 */
JS_EXTERN_API(void)
JS_FinishArenaPool(JSArenaPool *pool);

/*
 * Compact all of the arenas in a pool so that no space is wasted.
 */
JS_EXTERN_API(void)
JS_CompactArenaPool(JSArenaPool *pool);

/*
 * Finish using arenas, freeing all memory associated with them.
 */
JS_EXTERN_API(void)
JS_ArenaFinish(void);

/*
 * Friend functions used by the JS_ARENA_*() macros.
 */
JS_EXTERN_API(void *)
JS_ArenaAllocate(JSArenaPool *pool, JSUint32 nb);

JS_EXTERN_API(void *)
JS_ArenaGrow(JSArenaPool *pool, void *p, JSUint32 size, JSUint32 incr);

JS_EXTERN_API(void)
JS_ArenaRelease(JSArenaPool *pool, char *mark);

#ifdef JS_ARENAMETER

#include <stdio.h>

JS_EXTERN_API(void)
JS_ArenaCountAllocation(JSArenaPool *pool, JSUint32 nb);

JS_EXTERN_API(void)
JS_ArenaCountInplaceGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr);

JS_EXTERN_API(void)
JS_ArenaCountGrowth(JSArenaPool *pool, JSUint32 size, JSUint32incr);

JS_EXTERN_API(void)
JS_ArenaCountRelease(JSArenaPool *pool, char *mark);

JS_EXTERN_API(void)
JS_ArenaCountRetract(JSArenaPool *pool, char *mark);

JS_EXTERN_API(void)
JS_DumpArenaStats(FILE *fp);

#else  /* !JS_ARENAMETER */

#define JS_ArenaCountAllocation(ap, nb)                 /* nothing */
#define JS_ArenaCountInplaceGrowth(ap, size, incr)      /* nothing */
#define JS_ArenaCountGrowth(ap, size, incr)             /* nothing */
#define JS_ArenaCountRelease(ap, mark)                  /* nothing */
#define JS_ArenaCountRetract(ap, mark)                  /* nothing */

#endif /* !JS_ARENAMETER */

JS_END_EXTERN_C

#endif /* jsarena_h___ */

**** End of jsarena.h. ****

**** Start of jsarray.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS array class.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsstr.h"

/* 2^32 - 1 as a number and a string */
#define MAXINDEX 4294967295u
#define MAXSTR   "4294967295"

/*
 * Determine if the id represents an array index.
 *
 * An id is an array index according to ECMA by (15.4):
 *
 * "Array objects give special treatment to a certain class of property names.
 * A property name P (in the form of a string value) is an array index if and
 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
 * to 2^32-1."
 *
 * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
 * except that by using signed 32-bit integers we miss the top half of the
 * valid range. This function checks the string representation itself; note
 * that calling a standard conversion routine might allow strings such as
 * "08" or "4.0" as array indices, which they are not.
 */
static JSBool
IdIsIndex(jsid id, jsuint *indexp)
{
    JSString *str;
    jschar *cp;

    if (JSVAL_IS_INT(id)) {
	jsint i;
	i = JSVAL_TO_INT(id);
	if (i < 0)
	    return JS_FALSE;
	*indexp = (jsuint)i;
	return JS_TRUE;
    }

    /* It must be a string. */
    str = JSVAL_TO_STRING(id);
    cp = str->chars;
    if (JS7_ISDEC(*cp) && str->length < sizeof(MAXSTR)) {
	jsuint index = JS7_UNDEC(*cp++);
	jsuint oldIndex = 0;
	jsint c = 0;
	if (index != 0) {
	    while (JS7_ISDEC(*cp)) {
		oldIndex = index;
		c = JS7_UNDEC(*cp);
		index = 10*index + c;
		cp++;
	    }
	}
	/* Make sure all characters were consumed and that it couldn't
	 * have overflowed.
	 */
	if (*cp == 0 &&
	     (oldIndex < (MAXINDEX / 10) ||
	      (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
	{
	    *indexp = index;
	    return JS_TRUE;
	}
    }
    return JS_FALSE;
}


static JSBool
ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp)
{
    jsint i;

    /*
     * It's only an array length if it's an int or a double.  Some relevant
     * ECMA language is 15.4.2.2 - 'If the argument len is a number, then the
     * length property of the newly constructed object is set to ToUint32(len)
     * - I take 'is a number' to mean 'typeof len' returns 'number' in
     * javascript.
     */
    if (JSVAL_IS_INT(v)) {
	i = JSVAL_TO_INT(v);
	/* jsuint cast does ToUint32 */
	if (lengthp)
	    *lengthp = (jsuint) i;
	return JS_TRUE;
    }
    if (JSVAL_IS_DOUBLE(v)) {
	/*
	 * XXXmccabe I'd love to add another check here, against
	 * js_DoubleToInteger(d) != d), so that ValueIsLength matches
	 * IdIsIndex, but it doesn't seem to follow from ECMA.
	 * (seems to be appropriate for IdIsIndex, though).
	 */
	return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);
    }
    return JS_FALSE;
}


static JSBool
ValueToIndex(JSContext *cx, jsval v, jsuint *lengthp)
{
    jsint i;

    if (JSVAL_IS_INT(v)) {
	i = JSVAL_TO_INT(v);
	/* jsuint cast does ToUint32. */
	if (lengthp)
	    *lengthp = (jsuint)i;
	return JS_TRUE;
    }
    return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);
}

JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
    jsid id;
    jsint i;
    jsval v;

    id = (jsid) cx->runtime->atomState.lengthAtom;
    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
	return JS_FALSE;

    /* Short-circuit, because js_ValueToECMAUint32 fails when
     * called during init time.
     */
    if (JSVAL_IS_INT(v)) {
	i = JSVAL_TO_INT(v);
	/* jsuint cast does ToUint32. */
	*lengthp = (jsuint)i;
	return JS_TRUE;
    }
    return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);
}

static JSBool
IndexToValue(JSContext *cx, jsuint length, jsval *vp)
{
    if (length <= JSVAL_INT_MAX) {
	*vp = INT_TO_JSVAL(length);
	return JS_TRUE;
    }
    return js_NewDoubleValue(cx, (jsdouble)length, vp);
}

static JSBool
IndexToId(JSContext *cx, jsuint length, jsid *idp)
{
    JSString *str;
    JSAtom *atom;

    if (length <= JSVAL_INT_MAX) {
	*idp = (jsid) INT_TO_JSVAL(length);
    } else {
	str = js_NumberToString(cx, (jsdouble)length);
	if (!str)
	    return JS_FALSE;
	atom = js_AtomizeString(cx, str, 0);
	if (!atom)
	    return JS_FALSE;
	*idp = (jsid)atom;

    }
    return JS_TRUE;
}

JSBool
js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
{
    jsval v;
    jsid id;

    if (!IndexToValue(cx, length, &v))
	return JS_FALSE;
    id = (jsid) cx->runtime->atomState.lengthAtom;
    return OBJ_SET_PROPERTY(cx, obj, id, &v);
}

JSBool
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
    JSErrorReporter older;
    jsid id;
    JSBool ok;
    jsval v;

    older = JS_SetErrorReporter(cx, NULL);
    id = (jsid) cx->runtime->atomState.lengthAtom;
    ok = OBJ_GET_PROPERTY(cx, obj, id, &v);
    JS_SetErrorReporter(cx, older);
    if (!ok)
	return JS_FALSE;
    return ValueIsLength(cx, v, lengthp);
}

/*
 * This get function is specific to Array.prototype.length and other array
 * instance length properties.  It calls back through the class get function
 * in case some magic happens there (see call_getProperty in jsfun.c).
 */
static JSBool
array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp);
}

static JSBool
array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsuint newlen, oldlen, slot;
    jsid id2;
    jsval junk;

    if (!ValueToIndex(cx, *vp, &newlen))
	return JS_FALSE;
    if (!js_GetLengthProperty(cx, obj, &oldlen))
	return JS_FALSE;
    for (slot = newlen; slot < oldlen; slot++) {
	if (!IndexToId(cx, slot, &id2))
	    return JS_FALSE;
	if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk))
	    return JS_FALSE;
    }
    return IndexToValue(cx, newlen, vp);
}

static JSBool
array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsuint index, length;

    if (!(IdIsIndex(id, &index)))
	return JS_TRUE;
    if (!js_GetLengthProperty(cx, obj, &length))
	return JS_FALSE;
    if (index >= length) {
	length = index + 1;
	return js_SetLengthProperty(cx, obj, length);
    }
    return JS_TRUE;
}

static JSBool
array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
    jsuint length;

    if (cx->version == JSVERSION_1_2) {
	if (!js_GetLengthProperty(cx, obj, &length))
	    return JS_FALSE;
	switch (type) {
	  case JSTYPE_NUMBER:
	    return IndexToValue(cx, length, vp);
	  case JSTYPE_BOOLEAN:
	    *vp = BOOLEAN_TO_JSVAL(length > 0);
	    return JS_TRUE;
	  default:
	    return JS_TRUE;
	}
    }
    return js_TryValueOf(cx, obj, type, vp);
}

JSClass js_ArrayClass = {
    "Array",
    0,
    array_addProperty, JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
    JS_EnumerateStub,  JS_ResolveStub,    array_convert,     JS_FinalizeStub
};

static JSBool
array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
	       jsval *rval)
{
    JSBool ok;
    jsval v;
    jsuint length, index;
    jschar *chars, *ochars;
    size_t nchars, growth, seplen;
    const jschar *sepstr;
    JSString *str;
    JSHashEntry *he;

    ok = js_GetLengthProperty(cx, obj, &length);
    if (!ok)
	return JS_FALSE;
    ok = JS_TRUE;

    if (literalize) {
	he = js_EnterSharpObject(cx, obj, NULL, &chars);
	if (!he)
	    return JS_FALSE;
	if (IS_SHARP(he)) {
#if JS_HAS_SHARP_VARS
	    nchars = js_strlen(chars);
#else
	    chars[0] = '[';
	    chars[1] = ']';
	    chars[2] = 0;
	    nchars = 2;
#endif
	    goto make_string;
	}

	/*
	 * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the
	 * terminating 0.
	 */
	growth = (1 + 3 + 1) * sizeof(jschar);
	if (!chars) {
	    nchars = 0;
	    chars = malloc(growth);
	    if (!chars)
		goto done;
	} else {
	    MAKE_SHARP(he);
	    nchars = js_strlen(chars);
	    chars = realloc((ochars = chars), nchars * sizeof(jschar) + growth);
	    if (!chars) {
		free(ochars);
		goto done;
	    }
	}
	chars[nchars++] = '[';
    } else {
	if (length == 0) {
	    *rval = JS_GetEmptyStringValue(cx);
	    return ok;
	}
	chars = NULL;
	nchars = 0;
    }
    sepstr = NULL;
    seplen = sep->length;

    v = JSVAL_NULL;
    for (index = 0; index < length; index++) {
	ok = JS_GetElement(cx, obj, index, &v);
	if (!ok)
	    goto done;

	if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) {
	    str = cx->runtime->emptyString;
	} else {
	    str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v);
	    if (!str) {
		ok = JS_FALSE;
		goto done;
	    }
	}

	/* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */
	growth = (nchars + (sepstr ? seplen : 0) +
		  str->length +
		  3 + 1) * sizeof(jschar);
	if (!chars) {
	    chars = malloc(growth);
	    if (!chars)
		goto done;
	} else {
	    chars = realloc((ochars = chars), growth);
	    if (!chars) {
		free(ochars);
		goto done;
	    }
	}

	if (sepstr) {
	    js_strncpy(&chars[nchars], sepstr, seplen);
	    nchars += seplen;
	}
	sepstr = sep->chars;

	js_strncpy(&chars[nchars], str->chars, str->length);
	nchars += str->length;
    }

  done:
    if (literalize) {
	if (chars) {
	    if (JSVAL_IS_VOID(v)) {
		chars[nchars++] = ',';
		chars[nchars++] = ' ';
	    }
	    chars[nchars++] = ']';
	}
	js_LeaveSharpObject(cx, NULL);
    }
    if (!ok) {
	if (chars)
	    free(chars);
	return ok;
    }

  make_string:
    if (!chars) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
    chars[nchars] = 0;
    str = js_NewString(cx, chars, nchars, 0);
    if (!str) {
	free(chars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static jschar   comma_space_ucstr[] = {',', ' ', 0};
static jschar   comma_ucstr[]       = {',', 0};
static JSString comma_space         = {2, comma_space_ucstr};
static JSString comma               = {1, comma_ucstr};

#if JS_HAS_TOSOURCE
static JSBool
array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	       jsval *rval)
{
    return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval);
}
#endif

static JSBool
array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	       jsval *rval)
{
    JSBool literalize;

    /*
     * JS1.2 arrays convert to array literals, with a comma followed by a space
     * between each element.
     */
    literalize = (cx->version == JSVERSION_1_2);
    return array_join_sub(cx, obj, literalize ? &comma_space : &comma,
			  literalize, rval);
}

static JSBool
array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;

    if (argc == 0)
	return array_join_sub(cx, obj, &comma, JS_FALSE, rval);
    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;
    argv[0] = STRING_TO_JSVAL(str);
    return array_join_sub(cx, obj, str, JS_FALSE, rval);
}

#if !JS_HAS_MORE_PERL_FUN
static JSBool
array_nyi(JSContext *cx, const char *what)
{
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_PROTO, what);
    return JS_FALSE;
}
#endif

static JSBool
InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector)
{
    jsval v;
    jsid id;
    jsuint index;

    if (!IndexToValue(cx, length, &v))
	return JS_FALSE;
    id = (jsid) cx->runtime->atomState.lengthAtom;
    if (!js_DefineProperty(cx, obj, id, v,
			   array_length_getter, array_length_setter,
			   JSPROP_PERMANENT,
			   NULL)) {
	  return JS_FALSE;
    }
    if (!vector)
	return JS_TRUE;
    for (index = 0; index < length; index++) {
	if (!IndexToId(cx, index, &id))
	    return JS_FALSE;
	if (!js_DefineProperty(cx, obj, id, vector[index],
			       JS_PropertyStub, JS_PropertyStub,
			       JSPROP_ENUMERATE,
			       NULL)) {
	    return JS_FALSE;
	}
    }
    return JS_TRUE;
}

static JSBool
array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsuint len, half, i;
    jsid id, id2;
    jsval v, v2;

    if (!js_GetLengthProperty(cx, obj, &len))
	return JS_FALSE;

    half = len / 2;
    for (i = 0; i < half; i++) {
	if (!IndexToId(cx, i, &id))
	    return JS_FALSE;
	if (!IndexToId(cx, len - i - 1, &id2))
	    return JS_FALSE;
	if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
	    return JS_FALSE;
	if (!OBJ_GET_PROPERTY(cx, obj, id2, &v2))
	    return JS_FALSE;
	if (!OBJ_SET_PROPERTY(cx, obj, id, &v2))
	    return JS_FALSE;
	if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
	    return JS_FALSE;
    }

    *rval = OBJECT_TO_JSVAL(obj);
    return JS_TRUE;
}

typedef struct QSortArgs {
    void         *vec;
    size_t       elsize;
    void         *pivot;
    JSComparator cmp;
    void         *arg;
} QSortArgs;

static void
js_qsort_r(QSortArgs *qa, int lo, int hi)
{
    void *pivot, *a, *b;
    int i, j;

    pivot = qa->pivot;
    while (lo < hi) {
	i = lo;
	j = hi;
	a = (char *)qa->vec + i * qa->elsize;
	memmove(pivot, a, qa->elsize);
	while (i < j) {
	    for (;;) {
		b = (char *)qa->vec + j * qa->elsize;
		if ((*qa->cmp)(b, pivot, qa->arg) <= 0)
		    break;
		j--;
	    }
	    memmove(a, b, qa->elsize);
	    while (i < j && (*qa->cmp)(a, pivot, qa->arg) <= 0) {
		i++;
		a = (char *)qa->vec + i * qa->elsize;
	    }
	    memmove(b, a, qa->elsize);
	}
	memmove(a, pivot, qa->elsize);
	if (i - lo < hi - i) {
	    js_qsort_r(qa, lo, i - 1);
	    lo = i + 1;
	} else {
	    js_qsort_r(qa, i + 1, hi);
	    hi = i - 1;
	}
    }
}

JSBool
js_qsort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg)
{
    void *pivot;
    QSortArgs qa;

    pivot = malloc(elsize);
    if (!pivot)
	return JS_FALSE;
    qa.vec = vec;
    qa.elsize = elsize;
    qa.pivot = pivot;
    qa.cmp = cmp;
    qa.arg = arg;
    js_qsort_r(&qa, 0, (int)(nel - 1));
    free(pivot);
    return JS_TRUE;
}

typedef struct CompareArgs {
    JSContext  *context;
    jsval      fval;
    JSBool     status;
} CompareArgs;

static int
sort_compare(const void *a, const void *b, void *arg)
{
    jsval av = *(const jsval *)a, bv = *(const jsval *)b;
    CompareArgs *ca = arg;
    JSContext *cx = ca->context;
    jsdouble cmp = -1;
    jsval fval, argv[2], rval;
    JSBool ok;

    fval = ca->fval;
    if (fval == JSVAL_NULL) {
	JSString *astr, *bstr;

	if (av == bv) {
	    cmp = 0;
	} else if (av == JSVAL_VOID || bv == JSVAL_VOID) {
	    /* Put undefined properties at the end. */
	    cmp = (av == JSVAL_VOID) ? 1 : -1;
	} else if ((astr = js_ValueToString(cx, av)) != NULL &&
		   (bstr = js_ValueToString(cx, bv)) != NULL) {
	    cmp = js_CompareStrings(astr, bstr);
	} else {
	    ca->status = JS_FALSE;
	}
    } else {
	argv[0] = av;
	argv[1] = bv;
	ok = js_CallFunctionValue(cx,
				  OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)),
				  fval, 2, argv, &rval);
	if (ok) {
	    ok = js_ValueToNumber(cx, rval, &cmp);
	    /* Clamp cmp to -1, 0, 1. */
	    if (JSDOUBLE_IS_NaN(cmp)) {
		/* XXX report some kind of error here?  ECMA talks about
		 * 'consistent compare functions' that don't return NaN, but is
		 * silent about what the result should be.  So we currently
		 * ignore it.
		 */
		cmp = 0;
	    } else if (cmp != 0) {
		cmp = cmp > 0 ? 1 : -1;
	    }
	} else {
	    ca->status = ok;
	}
    }
    return (int)cmp;
}

/* XXXmccabe do the sort helper functions need to take int?  (Or can we claim
 * that 2^32 * 32 is too large to worry about?)  Something dumps when I change
 * to unsigned int; is qsort using -1 as a fencepost?
 */
static JSBool
array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval fval;
    CompareArgs ca;
    jsuint len, i;
    jsval *vec;
    jsid id;

    if (argc > 0) {
	if (JSVAL_IS_PRIMITIVE(argv[0])) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_BAD_SORT_ARG);
	    return JS_FALSE;
	}
	fval = argv[0];
    } else {
	fval = JSVAL_NULL;
    }

    if (!js_GetLengthProperty(cx, obj, &len))
	return JS_FALSE;
    vec = JS_malloc(cx, (size_t) len * sizeof(jsval));
    if (!vec)
	return JS_FALSE;

    for (i = 0; i < len; i++) {
	ca.status = IndexToId(cx, i, &id);
	if (!ca.status)
	    goto out;
	ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]);
	if (!ca.status)
	    goto out;
    }

    ca.context = cx;
    ca.fval = fval;
    ca.status = JS_TRUE;
    if (!js_qsort(vec, (size_t) len, sizeof(jsval), sort_compare, &ca)) {
	JS_ReportOutOfMemory(cx);
	ca.status = JS_FALSE;
    }

    if (ca.status) {
	ca.status = InitArrayObject(cx, obj, len, vec);
	if (ca.status)
	    *rval = OBJECT_TO_JSVAL(obj);
    }
out:
    if (vec)
	JS_free(cx, vec);
    return ca.status;
}

#ifdef NOTYET
/*
 * From "Programming perl", Larry Wall and Randall L. Schwartz, Copyright XXX
 * O'Reilly & Associates, Inc., but with Java primitive type sizes for i, l,
 * and so on:
 *
 *  a   An ASCII string, will be null padded.
 *  A   An ASCII string, will be space padded.
 *  b   A bit string, low-to-high order.
 *  B   A bit string, high-to-low order.
 *  h   A hexadecimal string, low nybble first.
 *  H   A hexadecimal string, high nybble first.
 *  c   A signed char value.
 *  C   An unsigned char value.
 *  s   A signed short (16-bit) value.
 *  S   An unsigned short (16-bit) value.
 *  i   A signed integer (32-bit) value.
 *  I   An unsigned integer (32-bit) value.
 *  l   A signed long (64-bit) value.
 *  L   An unsigned long (64-bit) value.
 *  n   A short in "network" byte order.
 *  N   An integer in "network" byte order.
 *  f   A single-precision float in IEEE format.
 *  d   A double-precision float in IEEE format.
 *  p   A pointer to a string.
 *  x   A null byte.
 *  X   Back up one byte.
 *  @   Null-fill to absolute position.
 *  u   A uuencoded string.
 *
 * Each letter may be followed by a number giving the repeat count.  Together
 * the letter and repeat count make a field specifier.  Field specifiers may
 * be separated by whitespace, which will be ignored.
 */
static JSBool
array_pack(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
#else
    return array_nyi(cx, "pack");
#endif
}
#endif /* NOTYET */

static JSBool
array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
    jsuint length;
    uintN i;
    jsid id;

    if (!js_GetLengthProperty(cx, obj, &length))
	return JS_FALSE;
    for (i = 0; i < argc; i++) {
	if (!IndexToId(cx, length + i, &id))
	    return JS_FALSE;
	if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i]))
	    return JS_FALSE;
    }

    /*
     * If JS1.2, follow Perl4 by returning the last thing pushed.  Otherwise,
     * return the new array length.
     */
    length += argc;
    if (cx->version == JSVERSION_1_2) {
	*rval = argc ? argv[argc-1] : JSVAL_VOID;
    } else {
	if (!IndexToValue(cx, length, rval))
	    return JS_FALSE;
    }
    return js_SetLengthProperty(cx, obj, length);
#else
    return array_nyi(cx, "push");
#endif
}

static JSBool
array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
    jsuint index;
    jsid id;
    jsval junk;

    if (!js_GetLengthProperty(cx, obj, &index))
	return JS_FALSE;
    if (index > 0) {
	index--;
	if (!IndexToId(cx, index, &id))
	    return JS_FALSE;

	/* Get the to-be-deleted property's value into rval. */
	if (!OBJ_GET_PROPERTY(cx, obj, id, rval))
	    return JS_FALSE;

	if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))
	    return JS_FALSE;
    }
    return js_SetLengthProperty(cx, obj, index);
#else
    return array_nyi(cx, "pop");
#endif
}

static JSBool
array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
    jsuint length, i;
    jsid id, id2;
    jsval v, junk;

    if (!js_GetLengthProperty(cx, obj, &length))
	return JS_FALSE;
    if (length > 0) {
	length--;
	id = JSVAL_ZERO;

	/* Get the to-be-deleted property's value into rval ASAP. */
	if (!OBJ_GET_PROPERTY(cx, obj, id, rval))
	    return JS_FALSE;

	/*
	 * Slide down the array above the first element.
	 */
	if (length > 0) {
	    for (i = 1; i <= length; i++) {
		if (!IndexToId(cx, i, &id))
		    return JS_FALSE;
		if (!IndexToId(cx, i - 1, &id2))
		    return JS_FALSE;
		if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
		    return JS_FALSE;
		if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
		    return JS_FALSE;
	    }
	}

	/* Delete the only or last element. */
	if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))
	    return JS_FALSE;
    }
    return js_SetLengthProperty(cx, obj, length);
#else
    return array_nyi(cx, "shift");
#endif
}

static JSBool
array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
    jsuint length, last;
    uintN i;
    jsid id, id2;
    jsval v;

    if (!js_GetLengthProperty(cx, obj, &length))
	return JS_FALSE;
    if (argc > 0) {
	/* Slide up the array to make room for argc at the bottom. */
	if (length > 0) {
	    last = length;
	    while (last--) {
		if (!IndexToId(cx, last, &id))
		    return JS_FALSE;
		if (!IndexToId(cx, last + argc, &id2))
		    return JS_FALSE;
		if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
		    return JS_FALSE;
		if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
		    return JS_FALSE;
	    }
	}

	/* Copy from argv to the bottom of the array. */
	for (i = 0; i < argc; i++) {
	    if (!IndexToId(cx, i, &id))
		return JS_FALSE;
	    if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i]))
		return JS_FALSE;
	}

	/* Follow Perl by returning the new array length. */
	length += argc;
	if (!js_SetLengthProperty(cx, obj, length))
	    return JS_FALSE;
    }
    return IndexToValue(cx, length, rval);
#else
    return array_nyi(cx, "unshift");
#endif
}

static JSBool
array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
    jsuint length, begin, end, count, delta, last;
    uintN i;
    jsdouble d;
    jsid id, id2;
    jsval v;
    JSObject *obj2;

    /* Nothing to do if no args.  Otherwise lock and load length. */
    if (argc == 0)
	return JS_TRUE;
    if (!js_GetLengthProperty(cx, obj, &length))
	return JS_FALSE;

    /* Convert the first argument into a starting index. */
    if (!js_ValueToNumber(cx, *argv, &d))
	return JS_FALSE;
    d = js_DoubleToInteger(d);
    if (d < 0) {
	d += length;
	if (d < 0)
	    d = 0;
    } else if (d > length) {
	d = length;
    }
    begin = (jsuint)d; /* d has been clamped to uint32 */
    argc--;
    argv++;

    /* Convert the second argument from a count into a fencepost index. */
    delta = length - begin;
    if (argc == 0) {
	count = delta;
	end = length;
    } else {
	if (!js_ValueToNumber(cx, *argv, &d))
	    return JS_FALSE;
	d = js_DoubleToInteger(d);
	if (d < 0)
	    d = 0;
	else if (d > delta)
	    d = delta;
	count = (jsuint)d;
	end = begin + count;
	argc--;
	argv++;
    }

    if (count == 1 && cx->version == JSVERSION_1_2) {
	/*
	 * JS lacks "list context", whereby in Perl one turns the single
	 * scalar that's spliced out into an array just by assigning it to
	 * @single instead of $single, or by using it as Perl push's first
	 * argument, for instance.
	 *
	 * JS1.2 emulated Perl too closely and returned a non-Array for
	 * the single-splice-out case, requiring callers to test and wrap
	 * in [] if necessary.  So JS1.3, default, and other versions all
	 * return an array of length 1 for uniformity.
	 */
	if (!IndexToId(cx, begin, &id))
	    return JS_FALSE;
	if (!OBJ_GET_PROPERTY(cx, obj, id, rval))
	    return JS_FALSE;
    } else {
	if (cx->version != JSVERSION_1_2 || count > 0) {
	    /*
	     * Create a new array value to return.  Our ECMA v2 proposal specs
	     * that splice always returns an array value, even when given no
	     * arguments.  We think this is best because it eliminates the need
	     * for callers to do an extra test to handle the empty splice case.
	     */
	    obj2 = js_NewArrayObject(cx, 0, NULL);
	    if (!obj2)
		return JS_FALSE;
	    *rval = OBJECT_TO_JSVAL(obj2);

            /* If there are elements to remove, put them into the return value. */
            if (count > 0) {
                for (last = begin; last < end; last++) {
                    if (!IndexToId(cx, last, &id))
                        return JS_FALSE;
                    if (!IndexToId(cx, last - begin, &id2))
                        return JS_FALSE;
                    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
                        return JS_FALSE;
                    if (!OBJ_SET_PROPERTY(cx, obj2, id2, &v))
                        return JS_FALSE;
                }
            }
	}
    }

    /* Find the direction (up or down) to copy and make way for argv. */
    if (argc > count) {
	delta = (jsuint)argc - count;
	last = length;
	/* (uint) end could be 0, so can't use vanilla >= test */
	while (last-- > end) {
	    if (!IndexToId(cx, last, &id))
		return JS_FALSE;
	    if (!IndexToId(cx, last + delta, &id2))
		return JS_FALSE;
	    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
		return JS_FALSE;
	    if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
		return JS_FALSE;
	}
	length += delta;
    } else if (argc < count) {
	delta = count - (jsuint)argc;
	for (last = end; last < length; last++) {
	    if (!IndexToId(cx, last, &id))
		return JS_FALSE;
	    if (!IndexToId(cx, last - delta, &id2))
		return JS_FALSE;
	    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
		return JS_FALSE;
	    if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
		return JS_FALSE;
	}
	length -= delta;
    }

    /* Copy from argv into the hole to complete the splice. */
    for (i = 0; i < argc; i++) {
	if (!IndexToId(cx, begin + i, &id))
	    return JS_FALSE;
	if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i]))
	    return JS_FALSE;
    }

    /* Update length in case we deleted elements from the end. */
    return js_SetLengthProperty(cx, obj, length);
#else
    return array_nyi(cx, "splice");
#endif
}

#if JS_HAS_SEQUENCE_OPS
/*
 * Python-esque sequence operations.
 */
static JSBool
array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSObject *nobj, *aobj;
    jsuint slot, length, alength;
    jsid id, id2;
    jsval v;
    uintN i;

    nobj = js_NewArrayObject(cx, 0, NULL);
    if (!nobj)
	return JS_FALSE;

    /* Only add the first element as an array if it looks like one.  Treat the
     * target the same way as the arguments.
     */

    /* XXXmccabe Might make sense to recast all of this as a do-while. */
    if (js_HasLengthProperty(cx, obj, &length)) {
	for (slot = 0; slot < length; slot++) {
	    if (!IndexToId(cx, slot, &id))
		return JS_FALSE;
	    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
		return JS_FALSE;
	    if (!OBJ_SET_PROPERTY(cx, nobj, id, &v))
		return JS_FALSE;
	}
    } else {
	length = 1;
	v = OBJECT_TO_JSVAL(obj);
	if (!OBJ_SET_PROPERTY(cx, nobj, JSVAL_ZERO, &v))
	    return JS_FALSE;
    }

    for (i = 0; i < argc; i++) {
	v = argv[i];
	if (JSVAL_IS_OBJECT(v)) {
	    aobj = JSVAL_TO_OBJECT(v);
	    if (aobj && js_HasLengthProperty(cx, aobj, &alength)) {
		for (slot = 0; slot < alength; slot++) {
		    if (!IndexToId(cx, slot, &id))
			return JS_FALSE;
		    if (!IndexToId(cx, length + slot, &id2))
			return JS_FALSE;
		    if (!OBJ_GET_PROPERTY(cx, aobj, id, &v))
			return JS_FALSE;
		    if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v))
			return JS_FALSE;
		}
		length += alength;
		continue;
	    }
	}

	if (!IndexToId(cx, length, &id))
	    return JS_FALSE;
	if (!OBJ_SET_PROPERTY(cx, nobj, id, &v))
	    return JS_FALSE;
	length++;
    }

    *rval = OBJECT_TO_JSVAL(nobj);
    return JS_TRUE;
}

static JSBool
array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSObject *nobj;
    jsuint length, begin, end, slot;
    jsdouble d;
    jsid id, id2;
    jsval v;

    nobj = js_NewArrayObject(cx, 0, NULL);
    if (!nobj)
	return JS_FALSE;

    if (!js_GetLengthProperty(cx, obj, &length))
	return JS_FALSE;
    begin = 0;
    end = length;

    if (argc > 0) {
	if (!js_ValueToNumber(cx, argv[0], &d))
	    return JS_FALSE;
	d = js_DoubleToInteger(d);
	if (d < 0) {
	    d += length;
	    if (d < 0)
		d = 0;
	} else if (d > length) {
	    d = length;
	}
	begin = (jsuint)d;

	if (argc > 1) {
	    if (!js_ValueToNumber(cx, argv[1], &d))
		return JS_FALSE;
	    d = js_DoubleToInteger(d);
	    if (d < 0) {
		d += length;
		if (d < 0)
		    d = 0;
	    } else if (d > length) {
		d = length;
	    }
	    end = (jsuint)d;
	}
    }

    for (slot = begin; slot < end; slot++) {
	if (!IndexToId(cx, slot, &id))
	    return JS_FALSE;
	if (!IndexToId(cx, slot - begin, &id2))
	    return JS_FALSE;
	if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
	    return JS_FALSE;
	if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v))
	    return JS_FALSE;
    }
    *rval = OBJECT_TO_JSVAL(nobj);
    return JS_TRUE;
}
#endif /* JS_HAS_SEQUENCE_OPS */

static JSFunctionSpec array_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   array_toSource,         0},
#endif
    {js_toString_str,   array_toString,         0},

    /* Perl-ish methods. */
    {"join",            array_join,             1},
    {"reverse",         array_reverse,          0},
    {"sort",            array_sort,             1},
#ifdef NOTYET
    {"pack",            array_pack,             1},
#endif
    {"push",            array_push,             1},
    {"pop",             array_pop,              0},
    {"shift",           array_shift,            0},
    {"unshift",         array_unshift,          1},
    {"splice",          array_splice,           1},

    /* Python-esque sequence methods. */
#if JS_HAS_SEQUENCE_OPS
    {"concat",          array_concat,           0},
    {"slice",           array_slice,            0},
#endif

    {0}
};

static JSBool
Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsuint length;
    jsval *vector;

    /* If called without new, replace obj with a new Array object. */
    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(obj);
    }

    if (argc == 0) {
	length = 0;
	vector = NULL;
    } else if (cx->version != JSVERSION_1_2 &&
	       argc == 1 && ValueIsLength(cx, argv[0], &length))
    {
	/*
	 * Only use 1 arg as first element for version 1.2; for any other
	 * version (including 1.3), follow ECMA and use it as a length.
	 */
	vector = NULL;
    } else {
	length = (jsuint) argc;
	vector = argv;
    }
    return InitArrayObject(cx, obj, length, vector);
}

JSObject *
js_InitArrayClass(JSContext *cx, JSObject *obj)
{
    JSObject *proto;

    proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1,
			 NULL, array_methods, NULL, NULL);

    /* Initialize the Array prototype object so it gets a length property. */
    if (!proto || !InitArrayObject(cx, proto, 0, NULL))
	return NULL;
    return proto;
}

JSObject *
js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector)
{
    JSObject *obj;

    obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
    if (!obj)
	return NULL;
    if (!InitArrayObject(cx, obj, length, vector)) {
	cx->newborn[GCX_OBJECT] = NULL;
	return NULL;
    }
    return obj;
}

**** End of jsarray.c. ****

**** Start of jsarray.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsarray_h___
#define jsarray_h___
/*
 * JS Array interface.
 */
#include "jsprvtd.h"
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

extern JSClass js_ArrayClass;

extern JSObject *
js_InitArrayClass(JSContext *cx, JSObject *obj);

extern JSObject *
js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector);

extern JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);

extern JSBool
js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length);

extern JSBool
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);

/*
 * JS-specific qsort function.
 */
typedef int (*JSComparator)(const void *a, const void *b, void *arg);

extern JSBool
js_qsort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg);

JS_END_EXTERN_C

#endif /* jsarray_h___ */

**** End of jsarray.h. ****

**** Start of jsatom.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS atom table.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jshash.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsgc.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsstr.h"

/*
 * Keep this in sync with jspubtd.h -- an assertion below will insist that
 * its length match the JSType enum's JSTYPE_LIMIT limit value.
 */
char *js_type_str[] = {
    "undefined",
    "object",
    "function",
    "string",
    "number",
    "boolean",
};

char *js_boolean_str[] = {
    js_false_str,
    js_true_str
};

char   js_Array_str[]             = "Array";
char   js_Math_str[]              = "Math";
char   js_Object_str[]            = "Object";
char   js_anonymous_str[]         = "anonymous";
char   js_arguments_str[]         = "arguments";
char   js_arity_str[]             = "arity";
char   js_assign_str[]            = "assign";
char   js_callee_str[]            = "callee";
char   js_caller_str[]            = "caller";
char   js_class_prototype_str[]   = "prototype";
char   js_constructor_str[]       = "constructor";
char   js_count_str[]             = "__count__";
char   js_eval_str[]              = "eval";
char   js_index_str[]             = "index";
char   js_input_str[]             = "input";
char   js_length_str[]            = "length";
char   js_name_str[]              = "name";
char   js_parent_str[]            = "__parent__";
char   js_proto_str[]             = "__proto__";
char   js_toSource_str[]          = "toSource";
char   js_toString_str[]          = "toString";
char   js_valueOf_str[]           = "valueOf";

#define HASH_OBJECT(o)  ((JSHashNumber)(o) >> JSVAL_TAGBITS)
#define HASH_INT(i)     ((JSHashNumber)(i))
#define HASH_DOUBLE(dp) ((JSHashNumber)(((uint32*)(dp))[0] ^ ((uint32*)(dp))[1]))
#define HASH_BOOLEAN(b) ((JSHashNumber)(b))

JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_atom_key(const void *key)
{
    jsval v;
    jsdouble *dp;

    /* Order JSVAL_IS_* tests by likelihood of success. */
    v = (jsval)key;
    if (JSVAL_IS_STRING(v))
	return js_HashString(JSVAL_TO_STRING(v));
    if (JSVAL_IS_INT(v))
	return HASH_INT(JSVAL_TO_INT(v));
    if (JSVAL_IS_DOUBLE(v)) {
	dp = JSVAL_TO_DOUBLE(v);
	return HASH_DOUBLE(dp);
    }
    if (JSVAL_IS_OBJECT(v))
	return HASH_OBJECT(JSVAL_TO_OBJECT(v));
    if (JSVAL_IS_BOOLEAN(v))
	return HASH_BOOLEAN(JSVAL_TO_BOOLEAN(v));
    return (JSHashNumber)v;
}

JS_STATIC_DLL_CALLBACK(intN)
js_compare_atom_keys(const void *k1, const void *k2)
{
    jsval v1, v2;

    v1 = (jsval)k1, v2 = (jsval)k2;
    if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2))
	return !js_CompareStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2));
    if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) {
	double d1 = *JSVAL_TO_DOUBLE(v1);
	double d2 = *JSVAL_TO_DOUBLE(v2);
	if (JSDOUBLE_IS_NaN(d1))
	    return JSDOUBLE_IS_NaN(d2);
#ifdef XP_PC
	/* XXX MSVC miscompiles such that (NaN == 0) */
	if (JSDOUBLE_IS_NaN(d2))
	    return JS_FALSE;
#endif
	return d1 == d2;
    }
    return v1 == v2;
}

JS_STATIC_DLL_CALLBACK(int)
js_compare_stub(const void *v1, const void *v2)
{
    return 1;
}

JS_STATIC_DLL_CALLBACK(void *)
js_alloc_atom_space(void *priv, size_t size)
{
    return malloc(size);
}

JS_STATIC_DLL_CALLBACK(void)
js_free_atom_space(void *priv, void *item)
{
    free(item);
}

JS_STATIC_DLL_CALLBACK(JSHashEntry *)
js_alloc_atom(void *priv, const void *key)
{
    JSAtomState *state = priv;
    JSAtom *atom;

    atom = malloc(sizeof(JSAtom));
    if (!atom)
	return NULL;
#ifdef JS_THREADSAFE
    state->tablegen++;
#endif
    atom->entry.key = key;
    atom->entry.value = NULL;
    atom->flags = 0;
    atom->kwindex = -1;
    atom->number = state->number++;
    return &atom->entry;
}

JS_STATIC_DLL_CALLBACK(void)
js_free_atom(void *priv, JSHashEntry *he, uintN flag)
{
    if (flag != HT_FREE_ENTRY)
	return;
#ifdef JS_THREADSAFE
    ((JSAtomState *)priv)->tablegen++;
#endif
    free(he);
}

static JSHashAllocOps atom_alloc_ops = {
    js_alloc_atom_space,    js_free_atom_space,
    js_alloc_atom,          js_free_atom
};

#define JS_ATOM_HASH_SIZE   1024

JSBool
js_InitAtomState(JSContext *cx, JSAtomState *state)
{
    uintN i;

    state->runtime = cx->runtime;
    state->number = 0;
    state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key,
				   js_compare_atom_keys, js_compare_stub,
				   &atom_alloc_ops, state);
    if (!state->table) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
#ifdef JS_THREADSAFE
    js_NewLock(&state->lock);
    state->tablegen = 0;
#endif

#define FROB(lval,str) {                                                      \
    if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) {     \
	js_FreeAtomState(cx, state);                                          \
	return JS_FALSE;                                                      \
    }                                                                         \
}

    JS_ASSERT(sizeof js_type_str / sizeof js_type_str[0] == JSTYPE_LIMIT);
    for (i = 0; i < JSTYPE_LIMIT; i++)
	FROB(typeAtoms[i],        js_type_str[i]);

    FROB(booleanAtoms[0],         js_false_str);
    FROB(booleanAtoms[1],         js_true_str);
    FROB(nullAtom,                js_null_str);

    FROB(ArrayAtom,               js_Array_str);
    FROB(MathAtom,                js_Math_str);
    FROB(ObjectAtom,              js_Object_str);
    FROB(anonymousAtom,           js_anonymous_str);
    FROB(argumentsAtom,           js_arguments_str);
    FROB(arityAtom,               js_arity_str);
    FROB(assignAtom,              js_assign_str);
    FROB(calleeAtom,              js_callee_str);
    FROB(callerAtom,              js_caller_str);
    FROB(classPrototypeAtom,      js_class_prototype_str);
    FROB(constructorAtom,         js_constructor_str);
    FROB(countAtom,               js_count_str);
    FROB(indexAtom,               js_index_str);
    FROB(inputAtom,               js_input_str);
    FROB(lengthAtom,              js_length_str);
    FROB(nameAtom,                js_name_str);
    FROB(parentAtom,              js_parent_str);
    FROB(protoAtom,               js_proto_str);
    FROB(toSourceAtom,            js_toSource_str);
    FROB(toStringAtom,            js_toString_str);
    FROB(valueOfAtom,             js_valueOf_str);
    FROB(evalAtom,                js_eval_str);

#undef FROB

    return JS_TRUE;
}

void
js_FreeAtomState(JSContext *cx, JSAtomState *state)
{
    state->runtime = NULL;
    JS_HashTableDestroy(state->table);
    state->table = NULL;
    state->number = 0;
#ifdef JS_THREADSAFE
    js_DestroyLock(&state->lock);
#endif
}

typedef struct MarkArgs {
    JSRuntime       *runtime;
    JSGCThingMarker mark;
} MarkArgs;

JS_STATIC_DLL_CALLBACK(intN)
js_atom_marker(JSHashEntry *he, intN i, void *arg)
{
    JSAtom *atom;
    jsval key;
    MarkArgs *args;

    atom = (JSAtom *)he;
    if (atom->flags & ATOM_PINNED) {
	atom->flags |= ATOM_MARK;
	key = ATOM_KEY(atom);
	if (JSVAL_IS_GCTHING(key)) {
	    args = arg;
	    args->mark(args->runtime, JSVAL_TO_GCTHING(key));
	}
    }
    return HT_ENUMERATE_NEXT;
}

void
js_MarkAtomState(JSAtomState *state, JSGCThingMarker mark)
{
    MarkArgs args;

    args.runtime = state->runtime;
    args.mark = mark;
    JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args);
}

JS_STATIC_DLL_CALLBACK(intN)
js_atom_sweeper(JSHashEntry *he, intN i, void *arg)
{
    JSAtom *atom;

    atom = (JSAtom *)he;
    if (atom->flags & ATOM_MARK) {
	atom->flags &= ~ATOM_MARK;
	return HT_ENUMERATE_NEXT;
    }
    JS_ASSERT((atom->flags & ATOM_PINNED) == 0);
    atom->entry.key = NULL;
    atom->flags = 0;
    return HT_ENUMERATE_REMOVE;
}

void
js_SweepAtomState(JSAtomState *state)
{
    JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, NULL);
}

JS_STATIC_DLL_CALLBACK(intN)
js_atom_unpinner(JSHashEntry *he, intN i, void *arg)
{
    JSAtom *atom;

    atom = (JSAtom *)he;
    atom->flags &= ~ATOM_PINNED;
    return HT_ENUMERATE_NEXT;
}

void
js_UnpinPinnedAtoms(JSAtomState *state)
{
    JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL);
}

static JSAtom *
js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags)
{
    JSAtomState *state;
    JSHashTable *table;
    JSHashEntry *he, **hep;
    JSAtom *atom;

    state = &cx->runtime->atomState;
    JS_LOCK(&state->lock,cx);
    table = state->table;
    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
    if ((he = *hep) == NULL) {
	he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);
	if (!he) {
	    JS_ReportOutOfMemory(cx);
	    atom = NULL;
	    goto out;
	}
    }

    atom = (JSAtom *)he;
    atom->flags |= flags;
out:
    JS_UNLOCK(&state->lock,cx);
    return atom;
}

JSAtom *
js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags)
{
    jsval key;
    JSHashNumber keyHash;

    /* XXX must be set in the following order or MSVC1.52 will crash */
    keyHash = HASH_OBJECT(obj);
    key = OBJECT_TO_JSVAL(obj);
    return js_AtomizeHashedKey(cx, key, keyHash, flags);
}

JSAtom *
js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags)
{
    jsval key;
    JSHashNumber keyHash;

    key = BOOLEAN_TO_JSVAL(b);
    keyHash = HASH_BOOLEAN(b);
    return js_AtomizeHashedKey(cx, key, keyHash, flags);
}

JSAtom *
js_AtomizeInt(JSContext *cx, jsint i, uintN flags)
{
    jsval key;
    JSHashNumber keyHash;

    key = INT_TO_JSVAL(i);
    keyHash = HASH_INT(i);
    return js_AtomizeHashedKey(cx, key, keyHash, flags);
}

JSAtom *
js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags)
{
    jsdouble *dp;
    JSHashNumber keyHash;
    jsval key;
    JSAtomState *state;
    JSHashTable *table;
    JSHashEntry *he, **hep;
    JSAtom *atom;
#define ALIGNNUM ((1<<JSVAL_TAGBITS) < sizeof(double) ? sizeof(double) : (1<<JSVAL_TAGBITS))
    char alignbuf[2*ALIGNNUM];
    jsuword alignint = (jsuword)alignbuf;
    jsuword xtra = ALIGNNUM-(alignint%ALIGNNUM);
#undef ALIGNNUM
    dp = (jsdouble *)&alignbuf[xtra];
    *dp = d;
    keyHash = HASH_DOUBLE(dp);
    key = DOUBLE_TO_JSVAL(dp);
    state = &cx->runtime->atomState;
    JS_LOCK(&state->lock,cx);
    table = state->table;
    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
    if ((he = *hep) == NULL) {
#ifdef JS_THREADSAFE
	uint32 gen = state->tablegen;
#endif
	JS_UNLOCK(&state->lock,cx);
	if (!js_NewDoubleValue(cx, d, &key))
	    return NULL;
	JS_LOCK(&state->lock,cx);
#ifdef JS_THREADSAFE
	if (state->tablegen != gen) {
	    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
	    if ((he = *hep) != NULL) {
		atom = (JSAtom *)he;
		goto out;
	    }
	}
#endif
	he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);
	if (!he) {
	    JS_ReportOutOfMemory(cx);
	    atom = NULL;
	    goto out;
	}
    }

    atom = (JSAtom *)he;
    atom->flags |= flags;
out:
    JS_UNLOCK(&state->lock,cx);
    return atom;
}

JSAtom *
js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
{
    JSHashNumber keyHash;
    jsval key;
    JSAtomState *state;
    JSHashTable *table;
    JSHashEntry *he, **hep;
    JSAtom *atom;

    keyHash = js_HashString(str);
    key = STRING_TO_JSVAL(str);
    state = &cx->runtime->atomState;
    JS_LOCK(&state->lock,cx);
    table = state->table;
    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
    if ((he = *hep) == NULL) {
	if (flags & ATOM_TMPSTR) {
#ifdef JS_THREADSAFE
	    uint32 gen = state->tablegen;
#endif
	    JS_UNLOCK(&state->lock,cx);
	    flags &= ~ATOM_TMPSTR;
	    if (flags & ATOM_NOCOPY) {
		flags &= ~ATOM_NOCOPY;
		str = js_NewString(cx, str->chars, str->length, 0);
	    } else {
		str = js_NewStringCopyN(cx, str->chars, str->length, 0);
	    }
	    if (!str)
		return NULL;
	    key = STRING_TO_JSVAL(str);
	    JS_LOCK(&state->lock,cx);
#ifdef JS_THREADSAFE
	    if (state->tablegen != gen) {
		hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
		if ((he = *hep) != NULL) {
		    atom = (JSAtom *)he;
		    goto out;
		}
	    }
#endif
	}
	he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);
	if (!he) {
	    JS_ReportOutOfMemory(cx);
	    atom = NULL;
	    goto out;
	}
    }

    atom = (JSAtom *)he;
    atom->flags |= flags;
out:
    JS_UNLOCK(&state->lock,cx);
    return atom;
}

JS_FRIEND_API(JSAtom *)
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
{
    jschar *chars;
    JSString *str;
    JSAtom *atom;
#define ALIGNNUM ((1<<JSVAL_TAGBITS) < sizeof(JSString) ? sizeof(JSString) : (1<<JSVAL_TAGBITS))
    char alignbuf[2*ALIGNNUM];
    jsuword alignint = (jsuword)alignbuf;
    jsuword xtra = ALIGNNUM-(alignint%ALIGNNUM);
#undef ALIGNNUM
    str = (JSString *)&alignbuf[xtra];
    chars = js_InflateString(cx, bytes, length);
    if (!chars)
	return NULL;
    str->chars = chars;
    str->length = length;
    atom = js_AtomizeString(cx, str, ATOM_TMPSTR | ATOM_NOCOPY | flags);
    if (!atom || ATOM_TO_STRING(atom)->chars != chars)
	JS_free(cx, chars);
    return atom;
}

JS_FRIEND_API(JSAtom *)
js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)
{
    JSString *str;
#define ALIGNNUM ((1<<JSVAL_TAGBITS) < sizeof(JSString) ? sizeof(JSString) : (1<<JSVAL_TAGBITS))
    char alignbuf[2*ALIGNNUM];
    jsuword alignint = (jsuword)alignbuf;
    jsuword xtra = ALIGNNUM-(alignint%ALIGNNUM);
#undef ALIGNNUM
    str = (JSString *)&alignbuf[xtra];
    str->chars = (jschar *)chars;
    str->length = length;
    return js_AtomizeString(cx, str, ATOM_TMPSTR | flags);
}

JSAtom *
js_AtomizeValue(JSContext *cx, jsval value, uintN flags)
{
    if (JSVAL_IS_STRING(value))
	return js_AtomizeString(cx, JSVAL_TO_STRING(value), flags);
    if (JSVAL_IS_INT(value))
	return js_AtomizeInt(cx, JSVAL_TO_INT(value), flags);
    if (JSVAL_IS_DOUBLE(value))
	return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(value), flags);
    if (JSVAL_IS_OBJECT(value))
	return js_AtomizeObject(cx, JSVAL_TO_OBJECT(value), flags);
    if (JSVAL_IS_BOOLEAN(value))
	return js_AtomizeBoolean(cx, JSVAL_TO_BOOLEAN(value), flags);
    return js_AtomizeHashedKey(cx, value, (JSHashNumber)value, flags);
}

JSAtom *
js_ValueToStringAtom(JSContext *cx, jsval v)
{
    JSString *str;

    str = js_ValueToString(cx, v);
    if (!str)
	return NULL;
    return js_AtomizeString(cx, str, 0);
}

JSAtomListElement *
js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
{
    JSAtomListElement *ale;

    ATOM_LIST_SEARCH(ale, al, atom);
    if (!ale) {
	JS_ARENA_ALLOCATE(ale, &cx->tempPool, sizeof(JSAtomListElement));
	if (!ale) {
	    JS_ReportOutOfMemory(cx);
	    return NULL;
	}
	ale->atom = atom;
	ale->index = (jsatomid) al->count++;
	ale->next = al->list;
	al->list = ale;
    }
    return ale;
}

JS_FRIEND_API(JSAtom *)
js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i)
{
    JSAtom *atom;

    JS_ASSERT(map->vector && i < map->length);
    if (!map->vector || i >= map->length) {
	char numBuf[12];
	JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_BAD_ATOMIC_NUMBER, numBuf);
	return NULL;
    }
    atom = map->vector[i];
    JS_ASSERT(atom);
    return atom;
}

JS_FRIEND_API(JSBool)
js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)
{
    JSAtom **vector;
    JSAtomListElement *ale, *next;
    uint32 count;

    ale = al->list;
    if (!ale) {
	map->vector = NULL;
	map->length = 0;
	return JS_TRUE;
    }

    count = al->count;
    if (count >= ATOM_INDEX_LIMIT) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_TOO_MANY_LITERALS);
	return JS_FALSE;
    }
    vector = JS_malloc(cx, (size_t) count * sizeof *vector);
    if (!vector)
	return JS_FALSE;

    do {
	vector[ale->index] = ale->atom;
	next = ale->next;
	ale->next = NULL;
    } while ((ale = next) != NULL);
    al->list = NULL;
    al->count = 0;

    map->vector = vector;
    map->length = (jsatomid)count;
    return JS_TRUE;
}

JS_FRIEND_API(void)
js_FreeAtomMap(JSContext *cx, JSAtomMap *map)
{
    if (map->vector) {
	free(map->vector);
	map->vector = NULL;
    }
    map->length = 0;
}

**** End of jsatom.c. ****

**** Start of jsatom.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsatom_h___
#define jsatom_h___
/*
 * JS atom table.
 */
#include <stddef.h>
#include "jstypes.h"
#include "jshash.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsprvtd.h"
#include "jspubtd.h"

#ifdef JS_THREADSAFE
#include "jslock.h"
#endif

JS_BEGIN_EXTERN_C

#define ATOM_NOCOPY     0x01            /* don't copy atom string bytes */
#define ATOM_TMPSTR     0x02            /* internal, to avoid extra string */
#define ATOM_MARK       0x04            /* atom is reachable via GC */
#define ATOM_PINNED     0x08            /* atom is pinned against GC */

struct JSAtom {
    JSHashEntry         entry;          /* key is jsval, value keyword info */
    uint8               flags;          /* flags, PINNED and/or MARK for now */
    int8                kwindex;        /* keyword index, -1 if not keyword */
    jsatomid            number;         /* atom serial number and hash code */
};

#define ATOM_KEY(atom)           ((jsval)(atom)->entry.key)
#define ATOM_IS_OBJECT(atom)     JSVAL_IS_OBJECT(ATOM_KEY(atom))
#define ATOM_TO_OBJECT(atom)     JSVAL_TO_OBJECT(ATOM_KEY(atom))
#define ATOM_IS_INT(atom)        JSVAL_IS_INT(ATOM_KEY(atom))
#define ATOM_TO_INT(atom)        JSVAL_TO_INT(ATOM_KEY(atom))
#define ATOM_IS_DOUBLE(atom)     JSVAL_IS_DOUBLE(ATOM_KEY(atom))
#define ATOM_TO_DOUBLE(atom)     JSVAL_TO_DOUBLE(ATOM_KEY(atom))
#define ATOM_IS_STRING(atom)     JSVAL_IS_STRING(ATOM_KEY(atom))
#define ATOM_TO_STRING(atom)     JSVAL_TO_STRING(ATOM_KEY(atom))
#define ATOM_IS_BOOLEAN(atom)    JSVAL_IS_BOOLEAN(ATOM_KEY(atom))
#define ATOM_TO_BOOLEAN(atom)    JSVAL_TO_BOOLEAN(ATOM_KEY(atom))
#define ATOM_BYTES(atom)         JS_GetStringBytes(ATOM_TO_STRING(atom))

struct JSAtomListElement {
    JSAtomListElement   *next;
    jsatomid            index;          /* index in script-specific atom map */
    JSAtom		*atom;
};

struct JSAtomList {
    JSAtomListElement   *list;          /* literals indexed for mapping */
    jsuint              count;          /* count of indexed literals */
};

#define ATOM_LIST_INIT(al)  ((al)->list = NULL, (al)->count = 0)

#define ATOM_LIST_SEARCH(_ale,_al,_atom)                                      \
    JS_BEGIN_MACRO                                                            \
	JSAtomListElement **_alep = &(_al)->list;                             \
	while ((_ale = *_alep) != NULL) {                                     \
	    if (_ale->atom == (_atom)) {                                      \
		/* Hit, move atom's element to the front of the list. */      \
		*_alep = _ale->next;                                          \
		_ale->next = (_al)->list;                                     \
		(_al)->list = _ale;                                           \
		break;                                                        \
	    }                                                                 \
	    _alep = &_ale->next;                                              \
	}                                                                     \
    JS_END_MACRO

struct JSAtomMap {
    JSAtom              **vector;       /* array of ptrs to indexed atoms */
    jsatomid            length;         /* count of (to-be-)indexed atoms */
};

struct JSAtomState {
    JSRuntime           *runtime;       /* runtime that owns us */
    JSHashTable         *table;         /* hash table containing all atoms */
    jsatomid            number;         /* one beyond greatest atom number */

    /* Type names and value literals. */
    JSAtom              *typeAtoms[JSTYPE_LIMIT];
    JSAtom              *booleanAtoms[2];
    JSAtom              *nullAtom;

    /* Various built-in or commonly-used atoms. */
    JSAtom              *ArrayAtom;
    JSAtom              *MathAtom;
    JSAtom              *ObjectAtom;
    JSAtom              *anonymousAtom;
    JSAtom              *argumentsAtom;
    JSAtom              *arityAtom;
    JSAtom              *assignAtom;
    JSAtom              *calleeAtom;
    JSAtom              *callerAtom;
    JSAtom              *classPrototypeAtom;
    JSAtom              *constructorAtom;
    JSAtom              *countAtom;
    JSAtom              *indexAtom;
    JSAtom              *inputAtom;
    JSAtom              *lengthAtom;
    JSAtom              *nameAtom;
    JSAtom              *parentAtom;
    JSAtom              *protoAtom;
    JSAtom              *toSourceAtom;
    JSAtom              *toStringAtom;
    JSAtom              *valueOfAtom;
    JSAtom              *evalAtom;

#ifdef JS_THREADSAFE
    JSThinLock          lock;
    volatile uint32     tablegen;
#endif
};

/* Well-known predefined strings and their atoms. */
extern char   *js_type_str[];
extern char   *js_boolean_str[];

extern char   js_Array_str[];
extern char   js_Math_str[];
extern char   js_Object_str[];
extern char   js_anonymous_str[];
extern char   js_arguments_str[];
extern char   js_arity_str[];
extern char   js_assign_str[];
extern char   js_callee_str[];
extern char   js_caller_str[];
extern char   js_class_prototype_str[];
extern char   js_constructor_str[];
extern char   js_count_str[];
extern char   js_eval_str[];
extern char   js_index_str[];
extern char   js_input_str[];
extern char   js_length_str[];
extern char   js_name_str[];
extern char   js_parent_str[];
extern char   js_proto_str[];
extern char   js_toSource_str[];
extern char   js_toString_str[];
extern char   js_valueOf_str[];

/*
 * Initialize atom state.  Return true on success, false with an out of
 * memory error report on failure.
 */
extern JSBool
js_InitAtomState(JSContext *cx, JSAtomState *state);

/*
 * Free and clear atom state.
 */
extern void
js_FreeAtomState(JSContext *cx, JSAtomState *state);

/*
 * Atom garbage collection hooks.
 */
typedef void
(*JSGCThingMarker)(JSRuntime *rt, void *thing);

extern void
js_MarkAtomState(JSAtomState *state, JSGCThingMarker mark);

extern void
js_SweepAtomState(JSAtomState *state);

extern void
js_UnpinPinnedAtoms(JSAtomState *state);

/*
 * Find or create the atom for an object.  If we create a new atom, give it the
 * type indicated in flags.  Return 0 on failure to allocate memory.
 */
extern JSAtom *
js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags);

/*
 * Find or create the atom for a Boolean value.  If we create a new atom, give
 * it the type indicated in flags.  Return 0 on failure to allocate memory.
 */
extern JSAtom *
js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags);

/*
 * Find or create the atom for an integer value.  If we create a new atom, give
 * it the type indicated in flags.  Return 0 on failure to allocate memory.
 */
extern JSAtom *
js_AtomizeInt(JSContext *cx, jsint i, uintN flags);

/*
 * Find or create the atom for a double value.  If we create a new atom, give
 * it the type indicated in flags.  Return 0 on failure to allocate memory.
 */
extern JSAtom *
js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags);

/*
 * Find or create the atom for a string.  If we create a new atom, give it the
 * type indicated in flags.  Return 0 on failure to allocate memory.
 */
extern JSAtom *
js_AtomizeString(JSContext *cx, JSString *str, uintN flags);

extern JS_FRIEND_API(JSAtom *)
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags);

extern JS_FRIEND_API(JSAtom *)
js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags);

/*
 * This variant handles all value tag types.
 */
extern JSAtom *
js_AtomizeValue(JSContext *cx, jsval value, uintN flags);

/*
 * Convert v to an atomized string.
 */
extern JSAtom *
js_ValueToStringAtom(JSContext *cx, jsval v);

/*
 * Assign atom an index and insert it on al.
 */
extern JSAtomListElement *
js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al);

/*
 * Get the atom with index i from map.
 */
extern JS_FRIEND_API(JSAtom *)
js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i);

/*
 * For all unmapped atoms recorded in al, add a mapping from the atom's index
 * to its address.  The GC must not run until all indexed atoms in atomLists
 * have been mapped by scripts connected to live objects (Function and Script
 * class objects have scripts as/in their private data -- the GC knows about
 * these two classes).
 */
extern JS_FRIEND_API(JSBool)
js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al);

/*
 * Free map->vector and clear map.
 */
extern JS_FRIEND_API(void)
js_FreeAtomMap(JSContext *cx, JSAtomMap *map);

JS_END_EXTERN_C

#endif /* jsatom_h___ */

**** End of jsatom.h. ****

**** Start of jsbit.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsbit_h___
#define jsbit_h___

#include "jstypes.h"
JS_BEGIN_EXTERN_C

/*
** A jsbitmap_t is a long integer that can be used for bitmaps
*/
typedef unsigned long jsbitmap_t;

#define JS_TEST_BIT(_map,_bit) \
    ((_map)[(_bit)>>JS_BITS_PER_LONG_LOG2] & (1L << ((_bit) & (JS_BITS_PER_LONG-1))))
#define JS_SET_BIT(_map,_bit) \
    ((_map)[(_bit)>>JS_BITS_PER_LONG_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_LONG-1))))
#define JS_CLEAR_BIT(_map,_bit) \
    ((_map)[(_bit)>>JS_BITS_PER_LONG_LOG2] &= ~(1L << ((_bit) & (JS_BITS_PER_LONG-1))))

/*
** Compute the log of the least power of 2 greater than or equal to n
*/
JS_EXTERN_API(JSIntn) JS_CeilingLog2(JSUint32 i); 

/*
** Compute the log of the greatest power of 2 less than or equal to n
*/
JS_EXTERN_API(JSIntn) JS_FloorLog2(JSUint32 i); 

/*
** Macro version of JS_CeilingLog2: Compute the log of the least power of
** 2 greater than or equal to _n. The result is returned in _log2.
*/
#define JS_CEILING_LOG2(_log2,_n)   \
  JS_BEGIN_MACRO                    \
    JSUint32 j_ = (JSUint32)(_n); 	\
    (_log2) = 0;                    \
    if ((j_) & ((j_)-1))            \
	(_log2) += 1;               \
    if ((j_) >> 16)                 \
	(_log2) += 16, (j_) >>= 16; \
    if ((j_) >> 8)                  \
	(_log2) += 8, (j_) >>= 8;   \
    if ((j_) >> 4)                  \
	(_log2) += 4, (j_) >>= 4;   \
    if ((j_) >> 2)                  \
	(_log2) += 2, (j_) >>= 2;   \
    if ((j_) >> 1)                  \
	(_log2) += 1;               \
  JS_END_MACRO

/*
** Macro version of JS_FloorLog2: Compute the log of the greatest power of
** 2 less than or equal to _n. The result is returned in _log2.
**
** This is equivalent to finding the highest set bit in the word.
*/
#define JS_FLOOR_LOG2(_log2,_n)   \
  JS_BEGIN_MACRO                    \
    JSUint32 j_ = (JSUint32)(_n); 	\
    (_log2) = 0;                    \
    if ((j_) >> 16)                 \
	(_log2) += 16, (j_) >>= 16; \
    if ((j_) >> 8)                  \
	(_log2) += 8, (j_) >>= 8;   \
    if ((j_) >> 4)                  \
	(_log2) += 4, (j_) >>= 4;   \
    if ((j_) >> 2)                  \
	(_log2) += 2, (j_) >>= 2;   \
    if ((j_) >> 1)                  \
	(_log2) += 1;               \
  JS_END_MACRO

JS_END_EXTERN_C
#endif /* jsbit_h___ */

**** End of jsbit.h. ****

**** Start of jsbool.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS boolean implementation.
 */
#include "jsstddef.h"
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsstr.h"

static JSClass boolean_class = {
    "Boolean",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
};

#if JS_HAS_TOSOURCE
#include "jsprf.h"

static JSBool
bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsval v;
    char buf[32];
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &boolean_class, argv))
	return JS_FALSE;
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_BOOLEAN(v))
	return js_obj_toSource(cx, obj, argc, argv, rval);
    JS_snprintf(buf, sizeof buf, "(new %s(%s))",
		boolean_class.name,
		js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]);
    str = JS_NewStringCopyZ(cx, buf);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#endif

static JSBool
bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsval v;
    JSAtom *atom;
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &boolean_class, argv))
	return JS_FALSE;
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_BOOLEAN(v))
	return js_obj_toString(cx, obj, argc, argv, rval);
    atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0];
    str = ATOM_TO_STRING(atom);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (!JS_InstanceOf(cx, obj, &boolean_class, argv))
	return JS_FALSE;
    *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    return JS_TRUE;
}

static JSFunctionSpec boolean_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   bool_toSource,          0},
#endif
    {js_toString_str,	bool_toString,		0},
    {js_valueOf_str,	bool_valueOf,		0},
    {0}
};

#ifdef XP_MAC
#undef Boolean
#define Boolean js_Boolean
#endif

static JSBool
Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSBool b;
    jsval bval;

    if (argc != 0) {
	if (!js_ValueToBoolean(cx, argv[0], &b))
	    return JS_FALSE;
	bval = BOOLEAN_TO_JSVAL(b);
    } else {
	bval = JSVAL_FALSE;
    }
    if (!cx->fp->constructing) {
	*rval = bval;
	return JS_TRUE;
    }
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval);
    return JS_TRUE;
}

JSObject *
js_InitBooleanClass(JSContext *cx, JSObject *obj)
{
    JSObject *proto;

    proto = JS_InitClass(cx, obj, NULL, &boolean_class, Boolean, 1,
			NULL, boolean_methods, NULL, NULL);
    if (!proto)
	return NULL;
    OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE);
    return proto;
}

JSObject *
js_BooleanToObject(JSContext *cx, JSBool b)
{
    JSObject *obj;

    obj = js_NewObject(cx, &boolean_class, NULL, NULL);
    if (!obj)
	return NULL;
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b));
    return obj;
}

JSString *
js_BooleanToString(JSContext *cx, JSBool b)
{
    return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);
}

JSBool
js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
{
    JSBool b;
    jsdouble d;

#if defined XP_PC && defined _MSC_VER && _MSC_VER <= 800
    /* MSVC1.5 coredumps */
    if (!bp)
	return JS_TRUE;
    /* This should be an if-else chain, but MSVC1.5 crashes if it is. */
#define ELSE
#else
#define ELSE else
#endif

    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
	/* Must return early to avoid falling thru to JSVAL_IS_OBJECT case. */
	*bp = JS_FALSE;
	return JS_TRUE;
    }
    if (JSVAL_IS_OBJECT(v)) {
	if (!JSVERSION_IS_ECMA(cx->version)) {
	    if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v))
		return JS_FALSE;
	    if (!JSVAL_IS_BOOLEAN(v))
		v = JSVAL_TRUE;		/* non-null object is true */
	    b = JSVAL_TO_BOOLEAN(v);
	} else {
	    b = JS_TRUE;
	}
    } ELSE
    if (JSVAL_IS_STRING(v)) {
	b = JSVAL_TO_STRING(v)->length ? JS_TRUE : JS_FALSE;
    } ELSE
    if (JSVAL_IS_INT(v)) {
	b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE;
    } ELSE
    if (JSVAL_IS_DOUBLE(v)) {
	d = *JSVAL_TO_DOUBLE(v);
	b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE;
    } ELSE
#if defined XP_PC && defined _MSC_VER && _MSC_VER <= 800
    if (JSVAL_IS_BOOLEAN(v)) {
	b = JSVAL_TO_BOOLEAN(v);
    }
#else
    {
        JS_ASSERT(JSVAL_IS_BOOLEAN(v));
	b = JSVAL_TO_BOOLEAN(v);
    }
#endif

#undef ELSE
    *bp = b;
    return JS_TRUE;
}

**** End of jsbool.c. ****

**** Start of jsbool.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsbool_h___
#define jsbool_h___
/*
 * JS boolean interface.
 */

JS_BEGIN_EXTERN_C

extern JSObject *
js_InitBooleanClass(JSContext *cx, JSObject *obj);

extern JSObject *
js_BooleanToObject(JSContext *cx, JSBool b);

extern JSString *
js_BooleanToString(JSContext *cx, JSBool b);

extern JSBool
js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp);

JS_END_EXTERN_C

#endif /* jsbool_h___ */

**** End of jsbool.h. ****

**** Start of jsclist.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsclist_h___
#define jsclist_h___

#include "jstypes.h"

/*
** Circular linked list
*/
typedef struct JSCListStr {
    struct JSCListStr *next;
    struct JSCListStr *prev;
} JSCList;

/*
** Insert element "_e" into the list, before "_l".
*/
#define JS_INSERT_BEFORE(_e,_l)	 \
    JS_BEGIN_MACRO		 \
	(_e)->next = (_l);	 \
	(_e)->prev = (_l)->prev; \
	(_l)->prev->next = (_e); \
	(_l)->prev = (_e);	 \
    JS_END_MACRO

/*
** Insert element "_e" into the list, after "_l".
*/
#define JS_INSERT_AFTER(_e,_l)	 \
    JS_BEGIN_MACRO		 \
	(_e)->next = (_l)->next; \
	(_e)->prev = (_l);	 \
	(_l)->next->prev = (_e); \
	(_l)->next = (_e);	 \
    JS_END_MACRO

/*
** Return the element following element "_e"
*/
#define JS_NEXT_LINK(_e)	 \
    	((_e)->next)
/*
** Return the element preceding element "_e"
*/
#define JS_PREV_LINK(_e)	 \
    	((_e)->prev)

/*
** Append an element "_e" to the end of the list "_l"
*/
#define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l)

/*
** Insert an element "_e" at the head of the list "_l"
*/
#define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l)

/* Return the head/tail of the list */
#define JS_LIST_HEAD(_l) (_l)->next
#define JS_LIST_TAIL(_l) (_l)->prev

/*
** Remove the element "_e" from it's circular list.
*/
#define JS_REMOVE_LINK(_e)	       \
    JS_BEGIN_MACRO		       \
	(_e)->prev->next = (_e)->next; \
	(_e)->next->prev = (_e)->prev; \
    JS_END_MACRO

/*
** Remove the element "_e" from it's circular list. Also initializes the
** linkage.
*/
#define JS_REMOVE_AND_INIT_LINK(_e)    \
    JS_BEGIN_MACRO		       \
	(_e)->prev->next = (_e)->next; \
	(_e)->next->prev = (_e)->prev; \
	(_e)->next = (_e);	       \
	(_e)->prev = (_e);	       \
    JS_END_MACRO

/*
** Return non-zero if the given circular list "_l" is empty, zero if the
** circular list is not empty
*/
#define JS_CLIST_IS_EMPTY(_l) \
    ((_l)->next == (_l))

/*
** Initialize a circular list
*/
#define JS_INIT_CLIST(_l)  \
    JS_BEGIN_MACRO	   \
	(_l)->next = (_l); \
	(_l)->prev = (_l); \
    JS_END_MACRO

#define JS_INIT_STATIC_CLIST(_l) \
    {(_l), (_l)}

#endif /* jsclist_h___ */

**** End of jsclist.h. ****

**** Start of jscntxt.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS execution context.
 */
#include "jsstddef.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsclist.h"
#include "jsprf.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsexn.h"
#include "jsgc.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscan.h"
#include "jsscript.h"

JSContext *
js_NewContext(JSRuntime *rt, size_t stacksize)
{
    JSContext *cx;

    cx = malloc(sizeof *cx);
    if (!cx)
	return NULL;
    memset(cx, 0, sizeof *cx);

    cx->runtime = rt;
#ifdef JS_THREADSAFE
    js_InitContextForLocking(cx);
#endif
    if (rt->contextList.next == (JSCList *)&rt->contextList) {
	/* First context on this runtime: initialize atoms and keywords. */
	if (!js_InitAtomState(cx, &rt->atomState) ||
	    !js_InitScanner(cx)) {
	    free(cx);
	    return NULL;
	}
    }
    /* Atomicly append cx to rt's context list. */
    JS_LOCK_RUNTIME_VOID(rt, JS_APPEND_LINK(&cx->links, &rt->contextList));

    cx->version = JSVERSION_DEFAULT;
    cx->jsop_eq = JSOP_EQ;
    cx->jsop_ne = JSOP_NE;
    JS_InitArenaPool(&cx->stackPool, "stack", stacksize, sizeof(jsval));
    JS_InitArenaPool(&cx->codePool, "code", 1024, sizeof(jsbytecode));
    JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));

#if JS_HAS_REGEXPS
    if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
	js_DestroyContext(cx);
	return NULL;
    }
#endif
#if JS_HAS_EXCEPTIONS
    cx->throwing = JS_FALSE;
#endif

    return cx;
}

void
js_DestroyContext(JSContext *cx)
{
    JSRuntime *rt;
    JSBool rtempty;

    rt = cx->runtime;

    /* Remove cx from context list first. */
    JS_LOCK_RUNTIME(rt);
    JS_REMOVE_LINK(&cx->links);
    rtempty = (rt->contextList.next == (JSCList *)&rt->contextList);
    JS_UNLOCK_RUNTIME(rt);

    if (rtempty) {
	/* Unpin all pinned atoms before final GC. */
	js_UnpinPinnedAtoms(&rt->atomState);

	/* Unlock GC things held by runtime pointers. */
	js_UnlockGCThing(cx, rt->jsNaN);
	js_UnlockGCThing(cx, rt->jsNegativeInfinity);
	js_UnlockGCThing(cx, rt->jsPositiveInfinity);
	js_UnlockGCThing(cx, rt->emptyString);

	/*
	 * Clear these so they get recreated if the standard classes are
	 * initialized again.
	 */
	rt->jsNaN = NULL;
	rt->jsNegativeInfinity = NULL;
	rt->jsPositiveInfinity = NULL;
	rt->emptyString = NULL;

	/* Clear debugging state to remove GC roots. */
	JS_ClearAllTraps(cx);
	JS_ClearAllWatchPoints(cx);
    }

    /* Remove more GC roots in regExpStatics, then collect garbage. */
#if JS_HAS_REGEXPS
    js_FreeRegExpStatics(cx, &cx->regExpStatics);
#endif
    js_ForceGC(cx);

    if (rtempty) {
	/* Free atom state now that we've run the GC. */
	js_FreeAtomState(cx, &rt->atomState);
    }

    /* Free the stuff hanging off of cx. */
    JS_FinishArenaPool(&cx->stackPool);
    JS_FinishArenaPool(&cx->codePool);
    JS_FinishArenaPool(&cx->tempPool);
    if (cx->lastMessage)
	free(cx->lastMessage);
    free(cx);
}

JSContext *
js_ContextIterator(JSRuntime *rt, JSContext **iterp)
{
    JSContext *cx = *iterp;

    JS_LOCK_RUNTIME(rt);
    if (!cx)
	cx = (JSContext *)rt->contextList.next;
    if ((void *)cx == &rt->contextList)
	cx = NULL;
    else
	*iterp = (JSContext *)cx->links.next;
    JS_UNLOCK_RUNTIME(rt);
    return cx;
}

void
js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
{
    JSStackFrame *fp;
    JSErrorReport report, *reportp;
    char *last;

    fp = cx->fp;
    if (fp && fp->script && fp->pc) {
	report.filename = fp->script->filename;
	report.lineno = js_PCToLineNumber(fp->script, fp->pc);
	/* XXX should fetch line somehow */
	report.linebuf = NULL;
	report.tokenptr = NULL;
	report.flags = flags;
	reportp = &report;
    } else {
	/* XXXshaver still fill out report here for flags? */
	reportp = NULL;
    }
    last = JS_vsmprintf(format, ap);
    if (!last)
	return;

    js_ReportErrorAgain(cx, last, reportp);
    free(last);
}

/*
 * The arguments from ap need to be packaged up into an array and stored
 * into the report struct.
 *
 * The format string addressed by the error number may contain operands
 * identified by the format {N}, where N is a decimal digit. Each of these
 * is to be replaced by the Nth argument from the va_list. The complete
 * message is placed into reportp->ucmessage converted to a JSString.
 *
 * returns true/false if the expansion succeeds (can fail for memory errors)
 */
JSBool
js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
			void *userRef, const uintN errorNumber,
			char **messagep, JSErrorReport *reportp,
                        JSBool charArgs, va_list ap)
{
    const JSErrorFormatString *fmtData;
    int i;
    int argCount;

    *messagep = NULL;
    if (callback) {
	fmtData = (*callback)(userRef, "Mountain View", errorNumber);
	if (fmtData != NULL) {
            int totalArgsLength = 0;
            int argLengths[10]; /* only {0} thru {9} supported */
	    argCount = fmtData->argCount;
            JS_ASSERT(argCount <= 10);
	    if (argCount > 0) {
		/*
                 * Gather the arguments into an array, and accumulate
                 * their sizes. We allocate 1 more than necessary and 
                 * null it out to act as the caboose when we free the
                 * pointers later.
		 */
		reportp->messageArgs
                        = JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
		if (!reportp->messageArgs)
		    return JS_FALSE;
                reportp->messageArgs[argCount] = NULL;
                for (i = 0; i < argCount; i++) {
                    if (charArgs) {
                        char *charArg = va_arg(ap, char *);
		        reportp->messageArgs[i] 
                            = js_InflateString(cx, charArg, strlen(charArg));
                    }
                    else
		        reportp->messageArgs[i] = va_arg(ap, jschar *);
                    argLengths[i] = js_strlen(reportp->messageArgs[i]);
                    totalArgsLength += argLengths[i];
                }
                /* NULL-terminate for easy copying. */
                reportp->messageArgs[i] = NULL; 
	    }
	    /*
	     * Parse the error format, substituting the argument X
	     * for {X} in the format.
	     */
	    if (argCount > 0) {
		if (fmtData->format) {
		    const char *fmt;
                    const jschar *arg;
		    jschar *out;
		    int expandedArgs = 0;
		    int expandedLength
			= strlen(fmtData->format)
			  - (3 * argCount) /* exclude the {n} */
                          + totalArgsLength;
            /* Note - the above calculation assumes that each argument
             *   is used once and only once in the expansion !!!
             */
		    reportp->ucmessage = out 
                        = JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
		    if (!out) {
			if (reportp->messageArgs) {
			    JS_free(cx, (void *)reportp->messageArgs);
			    reportp->messageArgs = NULL;
			}
			return JS_FALSE;
		    }
		    fmt = fmtData->format;
		    while (*fmt) {
			if (*fmt == '{') {	/* balance} */
			    if (isdigit(fmt[1])) {
				int d = JS7_UNDEC(fmt[1]);
				JS_ASSERT(expandedArgs < argCount);
				arg = reportp->messageArgs[d];
				js_strncpy(out, arg, argLengths[d]);
				out += argLengths[d];
				fmt += 3;
				expandedArgs++;
				continue;
			    }
			}
                        /*
                         * is this kosher?
                         */
			*out++ = (unsigned char)(*fmt++);
		    }
		    JS_ASSERT(expandedArgs == argCount);
		    *out = 0;
                    *messagep = js_DeflateString(cx, reportp->ucmessage,
                                                    out - reportp->ucmessage);
		}
            } else { /* 0 arguments, the format string
                        (if it exists) is the entire message */
                if (fmtData->format) {
		    *messagep = JS_strdup(cx, fmtData->format);
                    reportp->ucmessage 
                        = js_InflateString(cx, *messagep, strlen(*messagep));
                }
	    }
	}
    }
    if (*messagep == NULL) {
	/* where's the right place for this ??? */
	const char *defaultErrorMessage
	    = "No error message available for error number %d";
	size_t nbytes = strlen(defaultErrorMessage) + 16;
	*messagep = (char *)JS_malloc(cx, nbytes);
	JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
    }
    return JS_TRUE;
}

void
js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
			void *userRef, const uintN errorNumber,
                        JSBool charArgs, va_list ap)
{
    JSStackFrame *fp;
    JSErrorReport report;
    char *message;

    report.messageArgs = NULL;
    report.ucmessage = NULL;
    message = NULL;

    fp = cx->fp;
    if (fp && fp->script && fp->pc) {
	report.filename = fp->script->filename;
	report.lineno = js_PCToLineNumber(fp->script, fp->pc);
    } else {
	report.filename = NULL;
	report.lineno = 0;
    }

    /* XXX should fetch line somehow */
    report.linebuf = NULL;
    report.tokenptr = NULL;
    report.flags = flags;
    report.errorNumber = errorNumber;

    if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
				 &message, &report, charArgs, ap))
	return;

    /*
     * Check the error report, and set a JavaScript-catchable exception
     * if the error is defined to have an associated exception.  If an
     * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
     * on the error report, and exception-aware hosts should ignore it.

     * We set the JSREPORT_EXCEPTION flag for the special case of
     * user-thrown exeptions.
     */
    if (errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
        report.flags |= JSREPORT_EXCEPTION;
    
#if JS_HAS_ERROR_EXCEPTIONS
    /* 
     * Only call the error reporter if an exception wasn't raised. 
     *
     * If an exception was raised, then we call the debugErrorHook 
     * (if present) to give it a chance to see the error before it
     * propigates out of scope. This is needed for compatability with
     * the old scheme.
     */
    if (!js_ErrorToException(cx, message, &report))
        js_ReportErrorAgain(cx, message, &report);
    else if (cx->runtime->debugErrorHook && cx->errorReporter) {
        JSDebugErrorHook hook = cx->runtime->debugErrorHook;
        /* test local in case debugErrorHook changed on another thread */
        if (hook)
            hook(cx, message, &report, cx->runtime->debugErrorHookData);
    }
#else
    js_ReportErrorAgain(cx, message, &report);
#endif

    if (message)
	JS_free(cx, message);
    if (report.messageArgs) {
        int i = 0;
        while (report.messageArgs[i]) 
            JS_free(cx, (void *)report.messageArgs[i++]);
        JS_free(cx, (void *)report.messageArgs);
    }
    if (report.ucmessage) 
        JS_free(cx, (void *)report.ucmessage);
}

JS_FRIEND_API(void)
js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
{
    JSErrorReporter onError;

    if (!message)
	return;
    if (cx->lastMessage)
	free(cx->lastMessage);
    cx->lastMessage = JS_strdup(cx, message);
    if (!cx->lastMessage)
	return;
    onError = cx->errorReporter;
    /*
     * If debugErrorHook is present then we give it a chance to veto
     * sending the error on to the regular ErrorReporter.
     */
    if (cx->runtime->debugErrorHook && onError) {
        JSDebugErrorHook hook = cx->runtime->debugErrorHook;
        /* test local in case debugErrorHook changed on another thread */
        if (hook && !hook(cx, message, reportp,
                          cx->runtime->debugErrorHookData)) {
            onError = NULL;
        }
    }
    if (onError)
	(*onError)(cx, cx->lastMessage, reportp);
}

void
js_ReportIsNotDefined(JSContext *cx, const char *name)
{
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
}

#if defined DEBUG && defined XP_UNIX
/* For gdb usage. */
void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
#endif


JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
#if JS_HAS_DFLT_MSG_STRINGS
#define MSG_DEF(name, number, count, exception, format) \
    { format, count } ,
#else
#define MSG_DEF(name, number, count, exception, format) \
    { NULL, count } ,
#endif
#include "js.msg"
#undef MSG_DEF
};

const JSErrorFormatString *
js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
{
    if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
	    return &js_ErrorFormatString[errorNumber];
	else
	    return NULL;
}


**** End of jscntxt.c. ****

**** Start of jscntxt.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jscntxt_h___
#define jscntxt_h___
/*
 * JS execution context.
 */
#include "jsarena.h" /* Added by JSIFY */
#include "jsclist.h"
#include "jslong.h"
#include "jsatom.h"
#include "jsconfig.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jsobj.h"
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsregexp.h"

JS_BEGIN_EXTERN_C

struct JSRuntime {
    /* Garbage collector state, used by jsgc.c. */
    JSArenaPool         gcArenaPool;
    JSArenaPool         gcFlagsPool;
    JSHashTable         *gcRootsHash;
    JSGCThing           *gcFreeList;
    uint32              gcBytes;
    uint32              gcLastBytes;
    uint32              gcMaxBytes;
    uint32              gcLevel;
    uint32              gcNumber;
    JSBool              gcPoke;
    JSGCCallback        gcCallback;
#ifdef JS_GCMETER
    JSGCStats           gcStats;
#endif

    /* Literal table maintained by jsatom.c functions. */
    JSAtomState         atomState;

    /* Random number generator state, used by jsmath.c. */
    JSBool              rngInitialized;
    int64               rngMultiplier;
    int64               rngAddend;
    int64               rngMask;
    int64               rngSeed;
    jsdouble            rngDscale;

    /* Well-known numbers held for use by this runtime's contexts. */
    jsdouble            *jsNaN;
    jsdouble            *jsNegativeInfinity;
    jsdouble            *jsPositiveInfinity;

    /* Empty string held for use by this runtime's contexts. */
    JSString            *emptyString;

    /* List of active contexts sharing this runtime. */
    JSCList             contextList;

    /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */
    JSTrapHandler       interruptHandler;
    void                *interruptHandlerData;
    JSNewScriptHook     newScriptHook;
    void                *newScriptHookData;
    JSDestroyScriptHook destroyScriptHook;
    void                *destroyScriptHookData;
    JSTrapHandler       debuggerHandler;
    void                *debuggerHandlerData;
    JSSourceHandler     sourceHandler;
    void                *sourceHandlerData;
    JSInterpreterHook   executeHook;
    void                *executeHookData;
    JSInterpreterHook   callHook;
    void                *callHookData;
    JSObjectHook        objectHook;
    void                *objectHookData;
    JSTrapHandler       throwHook;
    void                *throwHookData;
    JSDebugErrorHook    debugErrorHook;
    void                *debugErrorHookData;

    /* More debugging state, see jsdbgapi.c. */
    JSCList             trapList;
    JSCList             watchPointList;

    /* Weak links to properties, indexed by quickened get/set opcodes. */
    /* XXX must come after JSCLists or MSVC alignment bug bites empty lists */
    JSPropertyCache     propertyCache;

#ifdef JS_THREADSAFE
    /* These combine to interlock the GC and new requests. */
    PRLock              *gcLock;
    PRCondVar           *gcDone;
    PRCondVar           *requestDone;
    uint32              requestCount;

    /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */
    JSThinLock          rtLock;
#endif
};

struct JSContext {
    JSCList             links;

    /* Interpreter activation count. */
    uintN               interpLevel;

    /* Runtime version control identifier and equality operators. */
    JSVersion           version;
    jsbytecode          jsop_eq;
    jsbytecode          jsop_ne;

    /* Data shared by threads in an address space. */
    JSRuntime           *runtime;

    /* Stack arena pool and frame pointer register. */
    JSArenaPool         stackPool;
    JSStackFrame        *fp;

    /* Temporary arena pools used while compiling and decompiling. */
    JSArenaPool         codePool;
    JSArenaPool         tempPool;

    /* Top-level object and pointer to top stack frame's scope chain. */
    JSObject            *globalObject;

    /* Most recently created things by type, members of the GC's root set. */
    JSGCThing           *newborn[GCX_NTYPES];

    /* Regular expression class statics (XXX not shared globally). */
    JSRegExpStatics     regExpStatics;

    /* State for object and array toSource conversion. */
    JSSharpObjectMap    sharpObjectMap;

    /* Last message string and trace file for debugging. */
    char                *lastMessage;
#ifdef DEBUG
    void                *tracefp;
#endif

    /* Per-context optional user callbacks. */
    JSBranchCallback    branchCallback;
    JSErrorReporter     errorReporter;

    /* Client opaque pointer */
    void                *data;

    /* GC and thread-safe state. */
    JSStackFrame        *dormantFrameChain; /* dormant stack frame to scan */
    uint32              gcDisabled;         /* XXX for pre-ECMAv2 switch */
#ifdef JS_THREADSAFE
    jsword              thread;
    jsrefcount          requestDepth;
    JSPackedBool        gcActive;
#endif

    /* Exception state (NB: throwing is packed with gcActive above). */
    JSPackedBool        throwing;           /* is there a pending exception? */
    jsval               exception;          /* most-recently-thrown exceptin */
};

extern JSContext *
js_NewContext(JSRuntime *rt, size_t stacksize);

extern void
js_DestroyContext(JSContext *cx);

extern JSContext *
js_ContextIterator(JSRuntime *rt, JSContext **iterp);

/*
 * Report an exception, which is currently realized as a printf-style format
 * string and its arguments.
 */
typedef enum JSErrNum {
#define MSG_DEF(name, number, count, exception, format) \
    name = number,
#include "js.msg"
#undef MSG_DEF
    JSErr_Limit
} JSErrNum;

extern const JSErrorFormatString *
js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);

#ifdef va_start
extern void
js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap);
extern void
js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
		       void *userRef, const uintN errorNumber,
                       JSBool charArgs, va_list ap);

extern JSBool
js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
			void *userRef, const uintN errorNumber,
			char **message, JSErrorReport *reportp,
                        JSBool charArgs, va_list ap);
#endif

/*
 * Report an exception using a previously composed JSErrorReport.
 */
extern JS_FRIEND_API(void)
js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report);

extern void
js_ReportIsNotDefined(JSContext *cx, const char *name);

extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];

JS_END_EXTERN_C

#endif /* jscntxt_h___ */

**** End of jscntxt.h. ****

**** Start of jscompat.h. ****

/* -*- Mode: C; tab-width: 8 -*-
 * Copyright  1996 Netscape Communications Corporation, All Rights Reserved.
 */
#ifndef jscompat_h___
#define jscompat_h___
/*
 * Compatibility glue for various NSPR versions.  We must always define int8,
 * int16, jsword, and so on to minimize differences with js/ref, no matter what
 * the NSPR typedef names may be.
 */
#ifndef jstypes_h___
	#include "jstypes.h"
#endif
#ifndef jslong_h___
	#include "jslong.h"
#endif

typedef JSIntn intN;
typedef JSUintn uintN;
typedef JSUword jsuword;
typedef JSWord jsword;
typedef float float32;
#define allocPriv allocPool
#endif /* jscompat_h___ */

**** End of jscompat.h. ****

**** Start of jsconfig.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS configuration macros.
 */
#ifndef JS_VERSION
#define JS_VERSION 140
#endif

#if JS_VERSION == 100

#define JS_BUG_AUTO_INDEX_PROPS 1       /* new object o: o.p = v sets o[0] */
#define JS_BUG_NULL_INDEX_PROPS 1       /* o[0] defaults to null, not void */
#define JS_BUG_EMPTY_INDEX_ZERO 1       /* o[""] is equivalent to o[0] */
#define JS_BUG_SHORT_CIRCUIT    1       /* 1 && 1 => true, 1 && 0 => 0 bug */
#define JS_BUG_EAGER_TOSTRING   1       /* o.toString() trumps o.valueOf() */
#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */
#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */
#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */
#define JS_BUG_FALLIBLE_EQOPS   1       /* fallible/intransitive equality ops */
#define JS_BUG_FALLIBLE_TONUM   1       /* fallible ValueToNumber primitive */
#define JS_BUG_WITH_CLOSURE     0       /* with(o)function f(){} sets o.f */
#define JS_BUG_SET_ENUMERATE    1       /* o.p=q flags o.p JSPROP_ENUMERATE */

#define JS_HAS_PROP_DELETE      0       /* delete o.p removes p from o */
#define JS_HAS_CALL_OBJECT      0       /* fun.caller is stack frame obj */
#define JS_HAS_LABEL_STATEMENT  0       /* has break/continue to label: */
#define JS_HAS_DO_WHILE_LOOP    0       /* has do {...} while (b) */
#define JS_HAS_SWITCH_STATEMENT 0       /* has switch (v) {case c: ...} */
#define JS_HAS_SOME_PERL_FUN    0       /* has array.join/reverse/sort */
#define JS_HAS_MORE_PERL_FUN    0       /* has array.push, str.substr, etc */
#define JS_HAS_VALUEOF_HINT     0       /* valueOf(hint) where hint is typeof */
#define JS_HAS_LEXICAL_CLOSURE  0       /* nested functions, lexically closed */
#define JS_HAS_APPLY_FUNCTION   0       /* has fun.apply(obj, argArray) */
#define JS_HAS_CALL_FUNCTION    0       /* has fun.call(obj, arg1, ... argN) */
#define JS_HAS_OBJ_PROTO_PROP   0       /* has o.__proto__ etc. */
#define JS_HAS_REGEXPS          0       /* has perl r.e.s via RegExp, /pat/ */
#define JS_HAS_SEQUENCE_OPS     0       /* has array.slice, string.concat */
#define JS_HAS_INITIALIZERS     0       /* has var o = {'foo': 42, 'bar':3} */
#define JS_HAS_OBJ_WATCHPOINT   0       /* has o.watch and o.unwatch */
#define JS_HAS_EXPORT_IMPORT    0       /* has export fun; import obj.fun */
#define JS_HAS_EVAL_THIS_SCOPE  0       /* Math.eval is same as with (Math) */
#define JS_HAS_TRIPLE_EQOPS     0       /* has === and !== identity eqops */
#define JS_HAS_SHARP_VARS       0       /* has #n=, #n# for object literals */
#define JS_HAS_REPLACE_LAMBDA   0       /* has string.replace(re, lambda) */
#define JS_HAS_SCRIPT_OBJECT    0       /* has (new Script("x++")).exec() */
#define JS_HAS_XDR		0	/* has XDR API and object methods */
#define JS_HAS_EXCEPTIONS	0	/* has exception handling */
#define JS_HAS_UNDEFINED        0       /* has global "undefined" property */
#define JS_HAS_TOSOURCE         0       /* has Object/Array toSource method */
#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT      0       /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 0       /* has hook for debugger keyword */
#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */
#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */
#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#elif JS_VERSION == 110

#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */
#define JS_BUG_NULL_INDEX_PROPS 1       /* o[0] defaults to null, not void */
#define JS_BUG_EMPTY_INDEX_ZERO 1       /* o[""] is equivalent to o[0] */
#define JS_BUG_SHORT_CIRCUIT    1       /* 1 && 1 => true, 1 && 0 => 0 bug */
#define JS_BUG_EAGER_TOSTRING   1       /* o.toString() trumps o.valueOf() */
#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */
#define JS_BUG_EVAL_THIS_FUN    1       /* eval('this') in function f is f */
#define JS_BUG_EVAL_THIS_SCOPE  1       /* Math.eval('sin(x)') vs. local x */
#define JS_BUG_FALLIBLE_EQOPS   1       /* fallible/intransitive equality ops */
#define JS_BUG_FALLIBLE_TONUM   1       /* fallible ValueToNumber primitive */
#define JS_BUG_WITH_CLOSURE     0       /* with(o)function f(){} sets o.f */
#define JS_BUG_SET_ENUMERATE    1       /* o.p=q flags o.p JSPROP_ENUMERATE */

#define JS_HAS_PROP_DELETE      0       /* delete o.p removes p from o */
#define JS_HAS_CALL_OBJECT      0       /* fun.caller is stack frame obj */
#define JS_HAS_LABEL_STATEMENT  0       /* has break/continue to label: */
#define JS_HAS_DO_WHILE_LOOP    0       /* has do {...} while (b) */
#define JS_HAS_SWITCH_STATEMENT 0       /* has switch (v) {case c: ...} */
#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */
#define JS_HAS_MORE_PERL_FUN    0       /* has array.push, str.substr, etc */
#define JS_HAS_VALUEOF_HINT     0       /* valueOf(hint) where hint is typeof */
#define JS_HAS_LEXICAL_CLOSURE  0       /* nested functions, lexically closed */
#define JS_HAS_APPLY_FUNCTION   0       /* has apply(fun, arg1, ... argN) */
#define JS_HAS_CALL_FUNCTION    0       /* has fun.call(obj, arg1, ... argN) */
#define JS_HAS_OBJ_PROTO_PROP   0       /* has o.__proto__ etc. */
#define JS_HAS_REGEXPS          0       /* has perl r.e.s via RegExp, /pat/ */
#define JS_HAS_SEQUENCE_OPS     0       /* has array.slice, string.concat */
#define JS_HAS_INITIALIZERS     0       /* has var o = {'foo': 42, 'bar':3} */
#define JS_HAS_OBJ_WATCHPOINT   0       /* has o.watch and o.unwatch */
#define JS_HAS_EXPORT_IMPORT    0       /* has export fun; import obj.fun */
#define JS_HAS_EVAL_THIS_SCOPE  0       /* Math.eval is same as with (Math) */
#define JS_HAS_TRIPLE_EQOPS     0       /* has === and !== identity eqops */
#define JS_HAS_SHARP_VARS       0       /* has #n=, #n# for object literals */
#define JS_HAS_REPLACE_LAMBDA   0       /* has string.replace(re, lambda) */
#define JS_HAS_SCRIPT_OBJECT    0       /* has (new Script("x++")).exec() */
#define JS_HAS_XDR		0	/* has XDR API and object methods */
#define JS_HAS_EXCEPTIONS	0	/* has exception handling */
#define JS_HAS_UNDEFINED        0       /* has global "undefined" property */
#define JS_HAS_TOSOURCE         0       /* has Object/Array toSource method */
#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT      0       /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 0       /* has hook for debugger keyword */
#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */
#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */
#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#elif JS_VERSION == 120

#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */
#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */
#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */
#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */
#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */
#define JS_BUG_VOID_TOSTRING    1       /* void 0 + 0 == "undefined0" */
#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */
#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */
#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */
#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */
#define JS_BUG_WITH_CLOSURE     1       /* with(o)function f(){} sets o.f */
#define JS_BUG_SET_ENUMERATE    1       /* o.p=q flags o.p JSPROP_ENUMERATE */

#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */
#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */
#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */
#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */
#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */
#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */
#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */
#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */
#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */
#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */
#define JS_HAS_CALL_FUNCTION    0       /* has fun.call(obj, arg1, ... argN) */
#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */
#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */
#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */
#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */
#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */
#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */
#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */
#define JS_HAS_TRIPLE_EQOPS     0       /* has === and !== identity eqops */
#define JS_HAS_SHARP_VARS       0       /* has #n=, #n# for object literals */
#define JS_HAS_REPLACE_LAMBDA   0       /* has string.replace(re, lambda) */
#define JS_HAS_SCRIPT_OBJECT    0       /* has (new Script("x++")).exec() */
#define JS_HAS_XDR		0	/* has XDR API and object methods */
#define JS_HAS_EXCEPTIONS	0	/* has exception handling */
#define JS_HAS_UNDEFINED        0       /* has global "undefined" property */
#define JS_HAS_TOSOURCE         0       /* has Object/Array toSource method */
#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT      0       /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 0       /* has hook for debugger keyword */
#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */
#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */
#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#elif JS_VERSION == 130

#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */
#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */
#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */
#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */
#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */
#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */
#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */
#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */
#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */
#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */
#define JS_BUG_WITH_CLOSURE     1       /* with(o)function f(){} sets o.f */
#define JS_BUG_SET_ENUMERATE    0       /* o.p=q flags o.p JSPROP_ENUMERATE */

#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */
#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */
#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */
#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */
#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */
#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */
#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */
#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */
#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */
#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */
#define JS_HAS_CALL_FUNCTION    1       /* has fun.call(obj, arg1, ... argN) */
#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */
#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */
#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */
#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */
#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */
#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */
#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */
#define JS_HAS_TRIPLE_EQOPS     1       /* has === and !== identity eqops */
#define JS_HAS_SHARP_VARS       1       /* has #n=, #n# for object literals */
#define JS_HAS_REPLACE_LAMBDA   1       /* has string.replace(re, lambda) */
#define JS_HAS_SCRIPT_OBJECT    1       /* has (new Script("x++")).exec() */
#define JS_HAS_XDR		1	/* has XDR API and object methods */
#define JS_HAS_EXCEPTIONS	0	/* has exception handling */
#define JS_HAS_UNDEFINED        1       /* has global "undefined" property */
#define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */
#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT      1       /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 1       /* has hook for debugger keyword */
#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */
#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */
#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#elif JS_VERSION == 140

#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */
#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */
#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */
#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */
#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */
#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */
#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */
#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */
#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */
#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */
#define JS_BUG_WITH_CLOSURE     1       /* with(o)function f(){} sets o.f */
#define JS_BUG_SET_ENUMERATE    0       /* o.p=q flags o.p JSPROP_ENUMERATE */

#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */
#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */
#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */
#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */
#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */
#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */
#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */
#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */
#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */
#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */
#define JS_HAS_CALL_FUNCTION    1       /* has fun.call(obj, arg1, ... argN) */
#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */
#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */
#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */
#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */
#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */
#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */
#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */
#define JS_HAS_TRIPLE_EQOPS     1       /* has === and !== identity eqops */
#define JS_HAS_SHARP_VARS       1       /* has #n=, #n# for object literals */
#define JS_HAS_REPLACE_LAMBDA   1       /* has string.replace(re, lambda) */
#define JS_HAS_SCRIPT_OBJECT    1       /* has (new Script("x++")).exec() */
#define JS_HAS_XDR		1	/* has XDR API and object methods */
#define JS_HAS_EXCEPTIONS	1	/* has exception handling */
#define JS_HAS_UNDEFINED        1       /* has global "undefined" property */
#define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */
#define JS_HAS_IN_OPERATOR      1       /* has in operator ('p' in {p:1}) */
#define JS_HAS_INSTANCEOF       1       /* has {p:1} instanceof Object */
#define JS_HAS_ARGS_OBJECT      1       /* has minimal ECMA arguments object */
#define JS_HAS_DEBUGGER_KEYWORD 1       /* has hook for debugger keyword */
#define JS_HAS_ERROR_EXCEPTIONS 0       /* rt errors reflected as exceptions */
#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */
#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#else

#error "unknown JS_VERSION"

#endif

**** End of jsconfig.h. ****

**** Start of jscpucfg.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * Generate CPU-specific bit-size and similar #defines.
 */
#include <stdio.h>

#ifdef sgi
#ifndef IRIX
# error "IRIX is not defined"
#endif
#endif

#ifdef __sun
#if defined(__svr4) || defined(__svr4__)
#ifndef SOLARIS
# error "SOLARIS is not defined"
#endif
#else
#ifndef SUNOS4
# error "SUNOS4 is not defined"
#endif
#endif
#endif

#ifdef __hpux
#ifndef HPUX
# error "HPUX is not defined"
#endif
#endif

#ifdef __osf__
#ifndef OSF1
# error "OSF1 is not defined"
#endif
#endif

#ifdef _IBMR2
#ifndef AIX
# error "AIX is not defined"
#endif
#endif

#ifdef bsdi
#ifndef BSDI
# error "BSDI is not defined"
#endif
#endif

#ifdef M_UNIX
#ifndef SCO
# error "SCO is not defined"
#endif
#endif

#if !defined(M_UNIX) && defined(_USLC_)
#ifndef UNIXWARE
# error "UNIXWARE is not defined"
#endif
#endif

#ifdef __MWERKS__
#define XP_MAC 1
#endif

/************************************************************************/

/* Generate cpucfg.h */
#ifdef XP_MAC
#include <Types.h>
#define INT64	UnsignedWide
#else
#ifdef XP_PC
#ifdef WIN32
#define INT64	_int64
#else
#define INT64	long
#endif
#else
#if defined(HPUX) || defined(SCO) || defined(UNIXWARE)
#define INT64	long
#else
#define INT64	long long
#endif
#endif
#endif

typedef void *prword;

struct align_short {
    char c;
    short a;
};
struct align_int {
    char c;
    int a;
};
struct align_long {
    char c;
    long a;
};
struct align_int64 {
    char c;
    INT64 a;
};
struct align_fakelonglong {
    char c;
    struct {
	long hi, lo;
    } a;
};
struct align_float {
    char c;
    float a;
};
struct align_double {
    char c;
    double a;
};
struct align_pointer {
    char c;
    void *a;
};
struct align_prword {
    char c;
    prword a;
};

#define ALIGN_OF(type) \
    (((char*)&(((struct align_##type *)0)->a)) - ((char*)0))

int bpb;

static int Log2(int n)
{
    int log2 = 0;

    if (n & (n-1))
	log2++;
    if (n >> 16)
	log2 += 16, n >>= 16;
    if (n >> 8)
	log2 += 8, n >>= 8;
    if (n >> 4)
	log2 += 4, n >>= 4;
    if (n >> 2)
	log2 += 2, n >>= 2;
    if (n >> 1)
	log2++;
    return log2;
}

/* We assume that int's are 32 bits */
static void do64(void)
{
    union {
	long i;
	char c[4];
    } u;

    u.i = 0x01020304;
    if (u.c[0] == 0x01) {
	printf("#undef  IS_LITTLE_ENDIAN\n");
	printf("#define IS_BIG_ENDIAN 1\n\n");
    } else {
	printf("#define IS_LITTLE_ENDIAN 1\n");
	printf("#undef  IS_BIG_ENDIAN\n\n");
    }
}

static void do32(void)
{
    union {
	long i;
	char c[4];
    } u;

    u.i = 0x01020304;
    if (u.c[0] == 0x01) {
	printf("#undef  IS_LITTLE_ENDIAN\n");
	printf("#define IS_BIG_ENDIAN 1\n\n");
    } else {
	printf("#define IS_LITTLE_ENDIAN 1\n");
	printf("#undef  IS_BIG_ENDIAN\n\n");
    }
}

/*
 * Conceivably this could actually be used, but there is lots of code out
 * there with ands and shifts in it that assumes a byte is exactly 8 bits,
 * so forget about porting THIS code to all those non 8 bit byte machines.
 */
static void BitsPerByte(void)
{
    bpb = 8;
}

int main(int argc, char **argv)
{
    BitsPerByte();

    printf("#ifndef js_cpucfg___\n");
    printf("#define js_cpucfg___\n\n");

    printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n");

    if (sizeof(long) == 8) {
	do64();
    } else {
	do32();
    }
    printf("#define JS_BYTES_PER_BYTE   %dL\n", sizeof(char));
    printf("#define JS_BYTES_PER_SHORT  %dL\n", sizeof(short));
    printf("#define JS_BYTES_PER_INT    %dL\n", sizeof(int));
    printf("#define JS_BYTES_PER_INT64  %dL\n", 8);
    printf("#define JS_BYTES_PER_LONG   %dL\n", sizeof(long));
    printf("#define JS_BYTES_PER_FLOAT  %dL\n", sizeof(float));
    printf("#define JS_BYTES_PER_DOUBLE %dL\n", sizeof(double));
    printf("#define JS_BYTES_PER_WORD   %dL\n", sizeof(prword));
    printf("#define JS_BYTES_PER_DWORD  %dL\n", 8);
    printf("\n");

    printf("#define JS_BITS_PER_BYTE    %dL\n", bpb);
    printf("#define JS_BITS_PER_SHORT   %dL\n", bpb * sizeof(short));
    printf("#define JS_BITS_PER_INT     %dL\n", bpb * sizeof(int));
    printf("#define JS_BITS_PER_INT64   %dL\n", bpb * 8);
    printf("#define JS_BITS_PER_LONG    %dL\n", bpb * sizeof(long));
    printf("#define JS_BITS_PER_FLOAT   %dL\n", bpb * sizeof(float));
    printf("#define JS_BITS_PER_DOUBLE  %dL\n", bpb * sizeof(double));
    printf("#define JS_BITS_PER_WORD    %dL\n", bpb * sizeof(prword));
    printf("\n");

    printf("#define JS_BITS_PER_BYTE_LOG2   %dL\n", Log2(bpb));
    printf("#define JS_BITS_PER_SHORT_LOG2  %dL\n", Log2(bpb * sizeof(short)));
    printf("#define JS_BITS_PER_INT_LOG2    %dL\n", Log2(bpb * sizeof(int)));
    printf("#define JS_BITS_PER_INT64_LOG2  %dL\n", 6);
    printf("#define JS_BITS_PER_LONG_LOG2   %dL\n", Log2(bpb * sizeof(long)));
    printf("#define JS_BITS_PER_FLOAT_LOG2  %dL\n", Log2(bpb * sizeof(float)));
    printf("#define JS_BITS_PER_DOUBLE_LOG2 %dL\n", Log2(bpb * sizeof(double)));
    printf("#define JS_BITS_PER_WORD_LOG2   %dL\n", Log2(bpb * sizeof(prword)));
    printf("\n");

    printf("#define JS_ALIGN_OF_SHORT   %dL\n", ALIGN_OF(short));
    printf("#define JS_ALIGN_OF_INT     %dL\n", ALIGN_OF(int));
    printf("#define JS_ALIGN_OF_LONG    %dL\n", ALIGN_OF(long));
    if (sizeof(INT64) < 8) {
	/* this machine doesn't actually support int64's */
	printf("#define JS_ALIGN_OF_INT64   %dL\n", ALIGN_OF(fakelonglong));
    } else {
	printf("#define JS_ALIGN_OF_INT64   %dL\n", ALIGN_OF(int64));
    }
    printf("#define JS_ALIGN_OF_FLOAT   %dL\n", ALIGN_OF(float));
    printf("#define JS_ALIGN_OF_DOUBLE  %dL\n", ALIGN_OF(double));
    printf("#define JS_ALIGN_OF_POINTER %dL\n", ALIGN_OF(pointer));
    printf("#define JS_ALIGN_OF_WORD    %dL\n", ALIGN_OF(prword));
    printf("\n");

    printf("#define JS_BYTES_PER_WORD_LOG2   %dL\n", Log2(sizeof(prword)));
    printf("#define JS_BYTES_PER_DWORD_LOG2  %dL\n", Log2(8));
    printf("#define JS_WORDS_PER_DWORD_LOG2  %dL\n", Log2(8/sizeof(prword)));
    printf("\n");

    printf("#endif /* js_cpucfg___ */\n");

    return 0;
}

**** End of jscpucfg.c. ****

**** Start of jscpucfg.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef js_cpucfg___
#define js_cpucfg___

#include "jsosdep.h"

#ifdef XP_MAC
#undef  IS_LITTLE_ENDIAN
#define IS_BIG_ENDIAN 1

#define JS_BYTES_PER_BYTE   1L
#define JS_BYTES_PER_SHORT  2L
#define JS_BYTES_PER_INT    4L
#define JS_BYTES_PER_INT64  8L
#define JS_BYTES_PER_LONG   4L
#define JS_BYTES_PER_FLOAT  4L
#define JS_BYTES_PER_DOUBLE 8L
#define JS_BYTES_PER_WORD   4L
#define JS_BYTES_PER_DWORD  8L

#define JS_BITS_PER_BYTE    8L
#define JS_BITS_PER_SHORT   16L
#define JS_BITS_PER_INT     32L
#define JS_BITS_PER_INT64   64L
#define JS_BITS_PER_LONG    32L
#define JS_BITS_PER_FLOAT   32L
#define JS_BITS_PER_DOUBLE  64L
#define JS_BITS_PER_WORD    32L

#define JS_BITS_PER_BYTE_LOG2   3L
#define JS_BITS_PER_SHORT_LOG2  4L
#define JS_BITS_PER_INT_LOG2    5L
#define JS_BITS_PER_INT64_LOG2  6L
#define JS_BITS_PER_LONG_LOG2   5L
#define JS_BITS_PER_FLOAT_LOG2  5L
#define JS_BITS_PER_DOUBLE_LOG2 6L
#define JS_BITS_PER_WORD_LOG2   5L

#define JS_ALIGN_OF_SHORT   2L
#define JS_ALIGN_OF_INT     4L
#define JS_ALIGN_OF_LONG    4L
#define JS_ALIGN_OF_INT64   2L
#define JS_ALIGN_OF_FLOAT   4L
#define JS_ALIGN_OF_DOUBLE  4L
#define JS_ALIGN_OF_POINTER 4L
#define JS_ALIGN_OF_WORD    4L

#define JS_BYTES_PER_WORD_LOG2   2L
#define JS_BYTES_PER_DWORD_LOG2  3L
#define PR_WORDS_PER_DWORD_LOG2  1L

#elif defined(XP_PC)

#ifdef _WIN32
#define IS_LITTLE_ENDIAN 1
#undef  IS_BIG_ENDIAN

#define JS_BYTES_PER_BYTE   1L
#define JS_BYTES_PER_SHORT  2L
#define JS_BYTES_PER_INT    4L
#define JS_BYTES_PER_INT64  8L
#define JS_BYTES_PER_LONG   4L
#define JS_BYTES_PER_FLOAT  4L
#define JS_BYTES_PER_DOUBLE 8L
#define JS_BYTES_PER_WORD   4L
#define JS_BYTES_PER_DWORD  8L

#define JS_BITS_PER_BYTE    8L
#define JS_BITS_PER_SHORT   16L
#define JS_BITS_PER_INT     32L
#define JS_BITS_PER_INT64   64L
#define JS_BITS_PER_LONG    32L
#define JS_BITS_PER_FLOAT   32L
#define JS_BITS_PER_DOUBLE  64L
#define JS_BITS_PER_WORD    32L

#define JS_BITS_PER_BYTE_LOG2   3L
#define JS_BITS_PER_SHORT_LOG2  4L
#define JS_BITS_PER_INT_LOG2    5L
#define JS_BITS_PER_INT64_LOG2  6L
#define JS_BITS_PER_LONG_LOG2   5L
#define JS_BITS_PER_FLOAT_LOG2  5L
#define JS_BITS_PER_DOUBLE_LOG2 6L
#define JS_BITS_PER_WORD_LOG2   5L

#define JS_ALIGN_OF_SHORT   2L
#define JS_ALIGN_OF_INT     4L
#define JS_ALIGN_OF_LONG    4L
#define JS_ALIGN_OF_INT64   8L
#define JS_ALIGN_OF_FLOAT   4L
#define JS_ALIGN_OF_DOUBLE  4L
#define JS_ALIGN_OF_POINTER 4L
#define JS_ALIGN_OF_WORD    4L

#define JS_BYTES_PER_WORD_LOG2   2L
#define JS_BYTES_PER_DWORD_LOG2  3L
#define PR_WORDS_PER_DWORD_LOG2  1L
#endif /* _WIN32 */

#if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */
#define IS_LITTLE_ENDIAN 1
#undef  IS_BIG_ENDIAN

#define JS_BYTES_PER_BYTE   1L
#define JS_BYTES_PER_SHORT  2L
#define JS_BYTES_PER_INT    2L
#define JS_BYTES_PER_INT64  8L
#define JS_BYTES_PER_LONG   4L
#define JS_BYTES_PER_FLOAT  4L
#define JS_BYTES_PER_DOUBLE 8L
#define JS_BYTES_PER_WORD   4L
#define JS_BYTES_PER_DWORD  8L

#define JS_BITS_PER_BYTE    8L
#define JS_BITS_PER_SHORT   16L
#define JS_BITS_PER_INT     16L
#define JS_BITS_PER_INT64   64L
#define JS_BITS_PER_LONG    32L
#define JS_BITS_PER_FLOAT   32L
#define JS_BITS_PER_DOUBLE  64L
#define JS_BITS_PER_WORD    32L

#define JS_BITS_PER_BYTE_LOG2   3L
#define JS_BITS_PER_SHORT_LOG2  4L
#define JS_BITS_PER_INT_LOG2    4L
#define JS_BITS_PER_INT64_LOG2  6L
#define JS_BITS_PER_LONG_LOG2   5L
#define JS_BITS_PER_FLOAT_LOG2  5L
#define JS_BITS_PER_DOUBLE_LOG2 6L
#define JS_BITS_PER_WORD_LOG2   5L

#define JS_ALIGN_OF_SHORT   2L
#define JS_ALIGN_OF_INT     2L
#define JS_ALIGN_OF_LONG    2L
#define JS_ALIGN_OF_INT64   2L
#define JS_ALIGN_OF_FLOAT   2L
#define JS_ALIGN_OF_DOUBLE  2L
#define JS_ALIGN_OF_POINTER 2L
#define JS_ALIGN_OF_WORD    2L

#define JS_BYTES_PER_WORD_LOG2   2L
#define JS_BYTES_PER_DWORD_LOG2  3L
#define PR_WORDS_PER_DWORD_LOG2  1L
#endif /* defined(_WINDOWS) && !defined(_WIN32) */

#elif defined(XP_UNIX)

#error "This file is supposed to be auto-generated on UNIX platforms, but the"
#error "static version for Mac and Windows platforms is being used."
#error "Something's probably wrong with paths/headers/dependencies/Makefiles."

#else

#error "Must define one of XP_MAC, XP_PC or XP_UNIX"

#endif

#endif /* js_cpucfg___ */

**** End of jscpucfg.h. ****

**** Start of jsdate.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS date methods.
 */
#include "jsstddef.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsprf.h"
#include "prmjtime.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsconfig.h"
#include "jscntxt.h"
#include "jsdate.h"
#include "jsinterp.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsstr.h"

/*
 * The JS 'Date' object is patterned after the Java 'Date' object.
 * Here is an script:
 *
 *    today = new Date();
 *
 *    print(today.toLocaleString());
 *
 *    weekDay = today.getDay();
 *
 *
 * These Java (and ECMA-262) methods are supported:
 *
 *     UTC
 *     getDate (getUTCDate)
 *     getDay (getUTCDay)
 *     getHours (getUTCHours)
 *     getMinutes (getUTCMinutes)
 *     getMonth (getUTCMonth)
 *     getSeconds (getUTCSeconds)
 *     getMilliseconds (getUTCMilliseconds)
 *     getTime
 *     getTimezoneOffset
 *     getYear
 *     getFullYear (getUTCFullYear)
 *     parse
 *     setDate (setUTCDate)
 *     setHours (setUTCHours)
 *     setMinutes (setUTCMinutes)
 *     setMonth (setUTCMonth)
 *     setSeconds (setUTCSeconds)
 *     setMilliseconds (setUTCMilliseconds)
 *     setTime
 *     setYear (setFullYear, setUTCFullYear)
 *     toGMTString (toUTCString)
 *     toLocaleString
 *     toString
 *
 *
 * These Java methods are not supported
 *
 *     setDay
 *     before
 *     after
 *     equals
 *     hashCode
 */

/*
 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
 * definition and reduce dependence on NSPR.  NSPR is used to get the current
 * time in milliseconds, the time zone offset, and the daylight savings time
 * offset for a given time.  NSPR is also used for Date.toLocaleString(), for
 * locale-specific formatting, and to get a string representing the timezone.
 * (Which turns out to be platform-dependent.)

 * To do:
 * (I did some performance tests by timing how long it took to run what
 *  I had of the js ECMA conformance tests.)
 *
 * - look at saving results across multiple calls to supporting
 * functions; the toString functions compute some of the same values
 * multiple times.  Although - I took a quick stab at this, and I lost
 * rather than gained.  (Fractionally.)  Hard to tell what compilers/processors
 * are doing these days.

 * - look at tweaking function return types to return double instead
 * of int; this seems to make things run slightly faster sometimes.
 * (though it could be architecture-dependent.)  It'd be good to see
 * how this does on win32.  (Tried it on irix.)  Types could use a
 * general going-over.  */

/*
 * Supporting functions - ECMA 15.9.1.*
 */

#define HalfTimeDomain  8.64e15
#define HoursPerDay     24.0
#define MinutesPerDay   (HoursPerDay * MinutesPerHour)
#define MinutesPerHour  60.0
#define SecondsPerDay   (MinutesPerDay * SecondsPerMinute)
#define SecondsPerHour  (MinutesPerHour * SecondsPerMinute)
#define SecondsPerMinute 60.0

#ifdef XP_PC
/* Work around msvc double optimization bug by making these runtime values; if
 * they're available at compile time, msvc optimizes division by them by
 * computing the reciprocal and multiplying instead of dividing - this loses
 * when the reciprocal isn't representable in a double.
 */
static jsdouble msPerSecond = 1000.0;
static jsdouble msPerDay = SecondsPerDay * 1000.0;
static jsdouble msPerHour = SecondsPerHour * 1000.0;
static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
#else
#define msPerDay        (SecondsPerDay * msPerSecond)
#define msPerHour       (SecondsPerHour * msPerSecond)
#define msPerMinute     (SecondsPerMinute * msPerSecond)
#define msPerSecond     1000.0
#endif

#define Day(t)          floor((t) / msPerDay)

static jsdouble
TimeWithinDay(jsdouble t)
{
    jsdouble result;
    result = fmod(t, msPerDay);
    if (result < 0)
	result += msPerDay;
    return result;
}

#define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \
			 ? 366 : 365)

/* math here has to be f.p, because we need
 *  floor((1968 - 1969) / 4) == -1
 */
#define DayFromYear(y)  (365 * ((y)-1970) + floor(((y)-1969)/4.0)            \
			 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
#define TimeFromYear(y) (DayFromYear(y) * msPerDay)

static jsint
YearFromTime(jsdouble t)
{
    jsint lo = (jsint) floor((t / msPerDay) / 366) + 1970;
    jsint hi = (jsint) floor((t / msPerDay) / 365) + 1970;
    jsint mid;

    /* above doesn't work for negative dates... */
    if (hi < lo) {
	jsint temp = lo;
	lo = hi;
	hi = temp;
    }

    /* Use a simple binary search algorithm to find the right
       year.  This seems like brute force... but the computation
       of hi and lo years above lands within one year of the
       correct answer for years within a thousand years of
       1970; the loop below only requires six iterations
       for year 270000. */
    while (hi > lo) {
	mid = (hi + lo) / 2;
	if (TimeFromYear(mid) > t) {
	    hi = mid - 1;
	} else {
	    if (TimeFromYear(mid) <= t) {
		jsint temp = mid + 1;
		if (TimeFromYear(temp) > t) {
		    return mid;
		}
		lo = mid + 1;
	    }
	}
    }
    return lo;
}

#define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)

#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))

/*
 * The following array contains the day of year for the first day of
 * each month, where index 0 is January, and day 0 is January 1.
 */
static jsdouble firstDayOfMonth[2][12] = {
    {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
    {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
};

#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];

static intN
MonthFromTime(jsdouble t)
{
    intN d, step;
    jsint year = YearFromTime(t);
    d = DayWithinYear(t, year);

    if (d < (step = 31))
	return 0;
    step += (InLeapYear(t) ? 29 : 28);
    if (d < step)
	return 1;
    if (d < (step += 31))
	return 2;
    if (d < (step += 30))
	return 3;
    if (d < (step += 31))
	return 4;
    if (d < (step += 30))
	return 5;
    if (d < (step += 31))
	return 6;
    if (d < (step += 31))
	return 7;
    if (d < (step += 30))
	return 8;
    if (d < (step += 31))
	return 9;
    if (d < (step += 30))
	return 10;
    return 11;
}

static intN
DateFromTime(jsdouble t)
{
    intN d, step, next;
    jsint year = YearFromTime(t);
    d = DayWithinYear(t, year);

    if (d <= (next = 30))
	return d + 1;
    step = next;
    next += (InLeapYear(t) ? 29 : 28);
    if (d <= next)
	return d - step;
    step = next;
    if (d <= (next += 31))
	return d - step;
    step = next;
    if (d <= (next += 30))
	return d - step;
    step = next;
    if (d <= (next += 31))
	return d - step;
    step = next;
    if (d <= (next += 30))
	return d - step;
    step = next;
    if (d <= (next += 31))
	return d - step;
    step = next;
    if (d <= (next += 31))
	return d - step;
    step = next;
    if (d <= (next += 30))
	return d - step;
    step = next;
    if (d <= (next += 31))
	return d - step;
    step = next;
    if (d <= (next += 30))
	return d - step;
    step = next;
    return d - step;
}

static intN
WeekDay(jsdouble t)
{
    jsint result;
    result = (jsint) Day(t) + 4;
    result = result % 7;
    if (result < 0)
	result += 7;
    return (intN) result;
}

/* LocalTZA gets set by js_InitDateClass() */
static jsdouble LocalTZA;

static jsdouble
DaylightSavingTA(jsdouble t)
{
    volatile int64 PR_t;
    int64 ms2us;
    int64 offset;
    jsdouble result;

    /* abort if NaN */
    if (JSDOUBLE_IS_NaN(t))
	return t;

    /* put our t in an LL, and map it to usec for prtime */
    JSLL_D2L(PR_t, t);
    JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
    JSLL_MUL(PR_t, PR_t, ms2us);

    offset = PRMJ_DSTOffset(PR_t);

    JSLL_DIV(offset, offset, ms2us);
    JSLL_L2D(result, offset);
    return result;
}

#define LocalTime(t)    ((t) + LocalTZA + DaylightSavingTA(t))

static jsdouble
UTC(jsdouble t)
{
    return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
}

static intN
HourFromTime(jsdouble t)
{
    intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
    if (result < 0)
	result += (intN)HoursPerDay;
    return result;
}

static intN
MinFromTime(jsdouble t)
{
    intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
    if (result < 0)
	result += (intN)MinutesPerHour;
    return result;
}

static intN
SecFromTime(jsdouble t)
{
    intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
    if (result < 0)
	result += (intN)SecondsPerMinute;
    return result;
}

static intN
msFromTime(jsdouble t)
{
    intN result = (intN) fmod(t, msPerSecond);
    if (result < 0)
	result += (intN)msPerSecond;
    return result;
}

#define MakeTime(hour, min, sec, ms) \
(((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)

static jsdouble
MakeDay(jsdouble year, jsdouble month, jsdouble date)
{
    jsdouble result;
    JSBool leap;
    jsdouble yearday;
    jsdouble monthday;

    year += floor(month / 12);

    month = fmod(month, 12);
    if (month < 0)
	month += 12;

    leap = (DaysInYear((jsint) year) == 366);

    yearday = floor(TimeFromYear(year) / msPerDay);
    monthday = DayFromMonth(month, leap);

    result = yearday
	     + monthday
	     + date - 1;
    return result;
}

#define MakeDate(day, time) (day * msPerDay + time)

#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
		      && !((d < 0 ? -d : d) > HalfTimeDomain)) \
		     ? js_DoubleToInteger(d + (+0.)) : *(cx->runtime->jsNaN))

/**
 * end of ECMA 'support' functions
 */

/*
 * Other Support routines and definitions
 */

static JSClass date_class = {
    "Date",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
};

/* for use by date_parse */

static char* wtb[] = {
    "am", "pm",
    "monday", "tuesday", "wednesday", "thursday", "friday",
    "saturday", "sunday",
    "january", "february", "march", "april", "may", "june",
    "july", "august", "september", "october", "november", "december",
    "gmt", "ut", "utc",
    "est", "edt",
    "cst", "cdt",
    "mst", "mdt",
    "pst", "pdt"
    /* time zone table needs to be expanded */
};

static int ttb[] = {
    0, 1, 0, 0, 0, 0, 0, 0, 0,
    2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
    10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
    10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
    10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
    10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
    10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
};

/* helper for date_parse */
static JSBool
date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
		   int count, int ignoreCase)
{
    JSBool result = JS_FALSE;
    /* return true if matches, otherwise, false */

    while (count > 0 && s1[s1off] && s2[s2off]) {
	if (ignoreCase) {
	    if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
		break;
	    }
	} else {
	    if ((jschar)s1[s1off] != s2[s2off]) {
		break;
	    }
	}
	s1off++;
	s2off++;
	count--;
    }

    if (count == 0) {
	result = JS_TRUE;
    }

    return result;
}

/* find UTC time from given date... no 1900 correction! */
static jsdouble
date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
		  jsdouble min, jsdouble sec, jsdouble msec)
{
    jsdouble day;
    jsdouble time;
    jsdouble result;

    day = MakeDay(year, mon, mday);
    time = MakeTime(hour, min, sec, msec);
    result = MakeDate(day, time);
    return result;
}

/*
 * See ECMA 15.9.4.[3-10];
 */
/* XXX this function must be above date_parseString to avoid a
   horrid bug in the Win16 1.52 compiler */
#define MAXARGS        7
static JSBool
date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble array[MAXARGS];
    uintN loop;
    jsdouble d;

    for (loop = 0; loop < MAXARGS; loop++) {
	if (loop < argc) {
	    if (!js_ValueToNumber(cx, argv[loop], &d))
		return JS_FALSE;
	    /* return NaN if any arg is NaN */
	    if (!JSDOUBLE_IS_FINITE(d)) {
		return js_NewNumberValue(cx, d, rval);
	    }
	    array[loop] = floor(d);
	} else {
	    array[loop] = 0;
	}
    }

    /* adjust 2-digit years into the 20th century */
    if (array[0] >= 0 && array[0] <= 99)
	array[0] += 1900;

    /* if we got a 0 for 'date' (which is out of range)
     * pretend it's a 1.  (So Date.UTC(1972, 5) works) */
    if (array[2] < 1)
	array[2] = 1;

    d = date_msecFromDate(array[0], array[1], array[2],
			      array[3], array[4], array[5], array[6]);
    d = TIMECLIP(d);

    return js_NewNumberValue(cx, d, rval);
}

static JSBool
date_parseString(const jschar *s, jsdouble *result)
{
    jsdouble msec;

    int year = -1;
    int mon = -1;
    int mday = -1;
    int hour = -1;
    int min = -1;
    int sec = -1;
    int c = -1;
    int i = 0;
    int n = -1;
    jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */
    int prevc = 0;
    int limit = 0;
    JSBool seenplusminus = JS_FALSE;

    if (s == 0)
	goto syntax;
    limit = js_strlen(s);
    while (i < limit) {
	c = s[i];
	i++;
	if (c <= ' ' || c == ',' || c == '-') {
	    if (c == '-' && '0' <= s[i] && s[i] <= '9') {
	      prevc = c;
	    }
	    continue;
	}
	if (c == '(') { /* comments) */
	    int depth = 1;
	    while (i < limit) {
		c = s[i];
		i++;
		if (c == '(') depth++;
		else if (c == ')')
		    if (--depth <= 0)
			break;
	    }
	    continue;
	}
	if ('0' <= c && c <= '9') {
	    n = c - '0';
	    while (i < limit && '0' <= (c = s[i]) && c <= '9') {
		n = n * 10 + c - '0';
		i++;
	    }

	    /* allow TZA before the year, so
	     * 'Wed Nov 05 21:49:11 GMT-0800 1997'
	     * works */

	    /* uses of seenplusminus allow : in TZA, so Java
	     * no-timezone style of GMT+4:30 works
	     */

	    if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
		/* make ':' case below change tzoffset */
		seenplusminus = JS_TRUE;

		/* offset */
		if (n < 24)
		    n = n * 60; /* EG. "GMT-3" */
		else
		    n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
		if (prevc == '+')       /* plus means east of GMT */
		    n = -n;
		if (tzoffset != 0 && tzoffset != -1)
		    goto syntax;
		tzoffset = n;
	    } else if (n >= 70  ||
		       (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
		if (year >= 0)
		    goto syntax;
		else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
		    year = n < 100 ? n + 1900 : n;
		else
		    goto syntax;
	    } else if (c == ':') {
		if (hour < 0)
		    hour = /*byte*/ n;
		else if (min < 0)
		    min = /*byte*/ n;
		else
		    goto syntax;
	    } else if (c == '/') {
		if (mon < 0)
		    mon = /*byte*/ n-1;
		else if (mday < 0)
		    mday = /*byte*/ n;
		else
		    goto syntax;
	    } else if (i < limit && c != ',' && c > ' ' && c != '-') {
		goto syntax;
	    } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
		if (tzoffset < 0)
		    tzoffset -= n;
		else
		    tzoffset += n;
	    } else if (hour >= 0 && min < 0) {
		min = /*byte*/ n;
	    } else if (min >= 0 && sec < 0) {
		sec = /*byte*/ n;
	    } else if (mday < 0) {
		mday = /*byte*/ n;
	    } else {
		goto syntax;
	    }
	    prevc = 0;
	} else if (c == '/' || c == ':' || c == '+' || c == '-') {
	    prevc = c;
	} else {
	    int st = i - 1;
	    int k;
	    while (i < limit) {
		c = s[i];
		if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
		    break;
		i++;
	    }
	    if (i <= st + 1)
		goto syntax;
	    for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
		if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
		    int action = ttb[k];
		    if (action != 0) {
			if (action == 1) { /* pm */
			    if (hour > 12 || hour < 0) {
				goto syntax;
			    } else {
				hour += 12;
			    }
			} else if (action <= 13) { /* month! */
			    if (mon < 0) {
				mon = /*byte*/ (action - 2);
			    } else {
				goto syntax;
			    }
			} else {
			    tzoffset = action - 10000;
			}
		    }
		    break;
		}
	    if (k < 0)
		goto syntax;
	    prevc = 0;
	}
    }
    if (year < 0 || mon < 0 || mday < 0)
	goto syntax;
    if (sec < 0)
	sec = 0;
    if (min < 0)
	min = 0;
    if (hour < 0)
	hour = 0;
    if (tzoffset == -1) { /* no time zone specified, have to use local */
	jsdouble time;
	time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);

	*result = UTC(time);
	return JS_TRUE;
    }

    msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
    msec += tzoffset * msPerMinute;
    *result = msec;
    return JS_TRUE;

syntax:
    /* syntax error */
    *result = 0;
    return JS_FALSE;
}

static JSBool
date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    jsdouble result;

    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;
    if (!date_parseString(str->chars, &result)) {
	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
	return JS_TRUE;
    }

    result = TIMECLIP(result);
    return js_NewNumberValue(cx, result, rval);
}

/*
 * Check that obj is an object of class Date, and get the date value.
 * Return NULL on failure.
 */
static jsdouble *
date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
{
    if (!JS_InstanceOf(cx, obj, &date_class, argv))
	return NULL;
    return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
}

/*
 * See ECMA 15.9.5.4 thru 15.9.5.23
 */
static JSBool
date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    return js_NewNumberValue(cx, *date, rval);
}

static JSBool
date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = YearFromTime(LocalTime(result));
    result -= 1900;
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		 jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = YearFromTime(LocalTime(result));
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		    jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = YearFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = MonthFromTime(LocalTime(result));
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		 jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = MonthFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = LocalTime(result);
    result = DateFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = DateFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = LocalTime(result);
    result = WeekDay(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	       jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = WeekDay(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = HourFromTime(LocalTime(result));
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		 jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = HourFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = MinFromTime(LocalTime(result));
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		   jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = MinFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

/* Date.getSeconds is mapped to getUTCSeconds */

static JSBool
date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = SecFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

/* Date.getMilliseconds is mapped to getUTCMilliseconds */

static JSBool
date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		     jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    result = msFromTime(result);
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		       jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    result = *date;

    /*
     * Return the time zone offset in minutes for the current locale
     * that is appropriate for this time. This value would be a
     * constant except for daylight savings time.
     */
    result = (result - LocalTime(result)) / msPerMinute;
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble result;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    if (!js_ValueToNumber(cx, argv[0], &result))
	return JS_FALSE;

    result = TIMECLIP(result);

    *date = result;
    return js_NewNumberValue(cx, result, rval);
}

static JSBool
date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      uintN maxargs, JSBool local, jsval *rval)
{
    uintN i;
    jsdouble args[4], *argp, *stop;
    jsdouble hour, min, sec, msec;
    jsdouble lorutime; /* Local or UTC version of *date */

    jsdouble time;
    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    result = *date;

    /* just return NaN if the date is already NaN */
    if (!JSDOUBLE_IS_FINITE(result))
	return js_NewNumberValue(cx, result, rval);

    /* Satisfy the ECMA rule that if a function is called with
     * fewer arguments than the specified formal arguments, the
     * remaining arguments are set to undefined.  Seems like all
     * the Date.setWhatever functions in ECMA are only varargs
     * beyond the first argument; this should be set to undefined
     * if it's not given.  This means that "d = new Date();
     * d.setMilliseconds()" returns NaN.  Blech.
     */
    if (argc == 0)
	argc = 1;   /* should be safe, because length of all settors is 1 */
    else if (argc > maxargs)
	argc = maxargs;  /* clamp argc */

    for (i = 0; i < argc; i++) {
	if (!js_ValueToNumber(cx, argv[i], &args[i]))
	    return JS_FALSE;
	if (!JSDOUBLE_IS_FINITE(args[i])) {
	    *date = *(cx->runtime->jsNaN);
	    return js_NewNumberValue(cx, *date, rval);
	}
	args[i] = js_DoubleToInteger(args[i]);
    }

    if (local)
	lorutime = LocalTime(result);
    else
	lorutime = result;

    argp = args;
    stop = argp + argc;
    if (maxargs >= 4 && argp < stop)
	hour = *argp++;
    else
	hour = HourFromTime(lorutime);

    if (maxargs >= 3 && argp < stop)
	min = *argp++;
    else
	min = MinFromTime(lorutime);

    if (maxargs >= 2 && argp < stop)
	sec = *argp++;
    else
	sec = SecFromTime(lorutime);

    if (maxargs >= 1 && argp < stop)
	msec = *argp;
    else
	msec = msFromTime(lorutime);

    time = MakeTime(hour, min, sec, msec);
    result = MakeDate(Day(lorutime), time);

/*     fprintf(stderr, "%f\n", result); */

    if (local)
	result = UTC(result);

/*     fprintf(stderr, "%f\n", result); */

    *date = TIMECLIP(result);
    return js_NewNumberValue(cx, *date, rval);
}

static JSBool
date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
		     jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
}

static JSBool
date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
			jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
}

static JSBool
date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
		jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
}

static JSBool
date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
		   jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
}

static JSBool
date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
		jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
}

static JSBool
date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
		   jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
}

static JSBool
date_setHours(JSContext *cx, JSObject *obj, uintN argc,
	      jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
}

static JSBool
date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
		 jsval *argv, jsval *rval)
{
    return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
}

static JSBool
date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
	      jsval *argv, uintN maxargs, JSBool local, jsval *rval)
{
    uintN i;
    jsdouble lorutime; /* local or UTC version of *date */
    jsdouble args[3], *argp, *stop;
    jsdouble year, month, day;
    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    result = *date;

    /* see complaint about ECMA in date_MakeTime */
    if (argc == 0)
	argc = 1;   /* should be safe, because length of all settors is 1 */
    else if (argc > maxargs)
	argc = maxargs;   /* clamp argc */

    for (i = 0; i < argc; i++) {
	if (!js_ValueToNumber(cx, argv[i], &args[i]))
	    return JS_FALSE;
	if (!JSDOUBLE_IS_FINITE(args[i])) {
	    *date = *(cx->runtime->jsNaN);
	    return js_NewNumberValue(cx, *date, rval);
	}
	args[i] = js_DoubleToInteger(args[i]);
    }

    /* return NaN if date is NaN and we're not setting the year,
     * If we are, use 0 as the time. */
    if (!(JSDOUBLE_IS_FINITE(result))) {
	if (argc < 3)
	    return js_NewNumberValue(cx, result, rval);
	else
	    lorutime = +0.;
    } else {
	if (local)
	    lorutime = LocalTime(result);
	else
	    lorutime = result;
    }

    argp = args;
    stop = argp + argc;
    if (maxargs >= 3 && argp < stop)
	year = *argp++;
    else
	year = YearFromTime(lorutime);

    if (maxargs >= 2 && argp < stop)
	month = *argp++;
    else
	month = MonthFromTime(lorutime);

    if (maxargs >= 1 && argp < stop)
	day = *argp++;
    else
	day = DateFromTime(lorutime);

    day = MakeDay(year, month, day); /* day within year */
    result = MakeDate(day, TimeWithinDay(lorutime));

    if (local)
	result = UTC(result);

    *date = TIMECLIP(result);
    return js_NewNumberValue(cx, *date, rval);
}

static JSBool
date_setDate(JSContext *cx, JSObject *obj, uintN argc,
	     jsval *argv, jsval *rval)
{
    return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
}

static JSBool
date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
		jsval *argv, jsval *rval)
{
    return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
}

static JSBool
date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
	      jsval *argv, jsval *rval)
{
    return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
}

static JSBool
date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
		 jsval *argv, jsval *rval)
{
    return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
}

static JSBool
date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
		 jsval *argv, jsval *rval)
{
    return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
}

static JSBool
date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
		    jsval *argv, jsval *rval)
{
    return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
}

static JSBool
date_setYear(JSContext *cx, JSObject *obj, uintN argc,
	     jsval *argv, jsval *rval)
{
    jsdouble t;
    jsdouble year;
    jsdouble day;
    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    result = *date;

    if (!js_ValueToNumber(cx, argv[0], &year))
	return JS_FALSE;
    if (!JSDOUBLE_IS_FINITE(year)) {
	*date = *(cx->runtime->jsNaN);
	return js_NewNumberValue(cx, *date, rval);
    }

    year = js_DoubleToInteger(year);

    if (!JSDOUBLE_IS_FINITE(result)) {
	t = +0.0;
    } else {
	t = LocalTime(result);
    }

    if (year >= 0 && year <= 99)
	year += 1900;

    day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
    result = MakeDate(day, TimeWithinDay(t));
    result = UTC(result);

    *date = TIMECLIP(result);
    return js_NewNumberValue(cx, *date, rval);
}

/* constants for toString, toUTCString */
static char js_NaN_date_str[] = "Invalid Date";
static const char* days[] =
{
   "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
};
static const char* months[] =
{
   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static JSBool
date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
		 jsval *argv, jsval *rval)
{
    char buf[100];
    JSString *str;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    if (!JSDOUBLE_IS_FINITE(*date)) {
	JS_snprintf(buf, sizeof buf, js_NaN_date_str);
    } else {
	jsdouble temp = *date;

	/* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
	 * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
	 */
	JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
		    days[WeekDay(temp)],
		    DateFromTime(temp),
		    months[MonthFromTime(temp)],
		    YearFromTime(temp),
		    HourFromTime(temp),
		    MinFromTime(temp),
		    SecFromTime(temp));
    }
    str = JS_NewStringCopyZ(cx, buf);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

/* for Date.toLocaleString; interface to PRMJTime date struct.
 * If findEquivalent is true, then try to map the year to an equivalent year
 * that's in range.
 */
static void
new_explode(jsdouble time, PRMJTime *split, JSBool findEquivalent)
{
    jsint year = YearFromTime(time);
    int16 adjustedYear;

    /* If the year doesn't fit in a PRMJTime, find something to do about it. */
    if (year > 32767 || year < -32768) {
	if (findEquivalent) {
	    /* We're really just trying to get a timezone string; map the year
	     * to some equivalent year in the range 0 to 2800.  Borrowed from
	     * A. D. Olsen.
	     */
	    jsint cycles;
#define CYCLE_YEARS 2800L
	    cycles = (year >= 0) ? year / CYCLE_YEARS
				 : -1 - (-1 - year) / CYCLE_YEARS;
	    adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
	} else {
	    /* Clamp it to the nearest representable year. */
	    adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
	}
    } else {
	adjustedYear = (int16)year;
    }

    split->tm_usec = (int32) msFromTime(time) * 1000;
    split->tm_sec = (int8) SecFromTime(time);
    split->tm_min = (int8) MinFromTime(time);
    split->tm_hour = (int8) HourFromTime(time);
    split->tm_mday = (int8) DateFromTime(time);
    split->tm_mon = (int8) MonthFromTime(time);
    split->tm_wday = (int8) WeekDay(time);
    split->tm_year = (int16) adjustedYear;
    split->tm_yday = (int16) DayWithinYear(time, year);

    /* not sure how this affects things, but it doesn't seem
       to matter. */
    split->tm_isdst = (DaylightSavingTA(time) != 0);
}

/* helper function */
static JSBool
date_format(JSContext *cx, jsdouble date, jsval *rval)
{
    char buf[100];
    JSString *str;
    char tzbuf[100];
    PRMJTime split;

    if (!JSDOUBLE_IS_FINITE(date)) {
	JS_snprintf(buf, sizeof buf, js_NaN_date_str);
    } else {
	jsdouble local = LocalTime(date);

	/* offset from GMT in minutes.  The offset includes daylight savings,
	   if it applies. */
	jsint minutes = (jsint) floor((LocalTZA + DaylightSavingTA(date))
				      / msPerMinute);

	/* map 510 minutes to 0830 hours */
	intN offset = (minutes / 60) * 100 + minutes % 60;

	/* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
	 * printed as 'GMT-0800' rather than as 'PST' to avoid
	 * operating-system dependence on strftime (which
	 * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
	 * PST as 'Pacific Standard Time.'  This way we always know
	 * what we're getting, and can parse it if we produce it.
	 * The OS TZA string is included as a comment.
	 */

	/* get a timezone string from the OS to include as a
	   comment. */
	new_explode(date, &split, JS_TRUE);
	PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z) ", &split);

	/* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
	 * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
	 */
	JS_snprintf(buf, sizeof buf, "%s %s %.2d %.2d:%.2d:%.2d GMT%+.4d %s%.4d",
		    days[WeekDay(local)],
		    months[MonthFromTime(local)],
		    DateFromTime(local),
		    HourFromTime(local),
		    MinFromTime(local),
		    SecFromTime(local),
		    offset,

		    /* don't print anything for the TZA comment if we got '()'
		     * or something non-parenthesized from the OS.
		     */
		    ((tzbuf[0] == '(' && tzbuf[1] != ')') ? tzbuf : ""),

		    YearFromTime(local));
    }

    str = JS_NewStringCopyZ(cx, buf);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
		    jsval *argv, jsval *rval)
{
    char buf[100];
    JSString *str;
    PRMJTime split;
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    if (!JSDOUBLE_IS_FINITE(*date)) {
	JS_snprintf(buf, sizeof buf, js_NaN_date_str);
    } else {
	intN result_len;
	jsdouble local = LocalTime(*date);
	new_explode(local, &split, JS_FALSE);

	/* let PRMJTime format it.  Use '%#c' for windows, because '%c' is
	 * backward-compatible and non-y2k with msvc; '%#c' requests that a
	 * full year be used in the result string.
	 */
	result_len = PRMJ_FormatTime(buf, sizeof buf,
#ifdef _WIN32
				   "%#c",
#else
				   "%c",
#endif
				   &split);

	/* If it failed, default to toString. */
	if (result_len == 0)
	    return date_format(cx, *date, rval);
    }

    str = JS_NewStringCopyZ(cx, buf);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

#if JS_HAS_TOSOURCE
#include "jsdtoa.h"

static JSBool
date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsdouble *date;
    char buf[32], *bytes;
    JSString *str;

    date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;

    JS_cnvtf(buf, sizeof buf, 20, *date);
    bytes = JS_smprintf("(new %s(%s))", date_class.name, buf);
    if (!bytes) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }

    str = JS_NewString(cx, bytes, strlen(bytes));
    if (!str) {
	free(bytes);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#endif

static JSBool
date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    jsdouble *date = date_getProlog(cx, obj, argv);
    if (!date)
	return JS_FALSE;
    return date_format(cx, *date, rval);
}

#if JS_HAS_VALUEOF_HINT
static JSBool
date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	     jsval *rval)
{
    /* It is an error to call date_valueOf on a non-date object, but we don't
     * need to check for that explicitly here because every path calls
     * date_getProlog, which does the check.
     */

    /* If called directly with no arguments, convert to a time number. */
    if (argc == 0)
	return date_getTime(cx, obj, argc, argv, rval);

    /* Convert to number only if the hint was given, otherwise favor string. */
    if (argc == 1) {
	JSString *str, *str2;

	str = js_ValueToString(cx, argv[0]);
	if (!str)
	    return JS_FALSE;
	str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
	if (!js_CompareStrings(str, str2))
	    return date_getTime(cx, obj, argc, argv, rval);
    }
    return date_toString(cx, obj, argc, argv, rval);
}
#else
#define date_valueOf date_getTime
#endif


/*
 * creation and destruction
 */

static JSFunctionSpec date_static_methods[] = {
    {"UTC",               date_UTC,               MAXARGS },
    {"parse",             date_parse,             1 },
    {0}
};

static JSFunctionSpec date_methods[] = {
    {"getTime",           date_getTime,           0 },
    {"getTimezoneOffset", date_getTimezoneOffset, 0 },
    {"getYear",           date_getYear,           0 },
    {"getFullYear",       date_getFullYear,       0 },
    {"getUTCFullYear",    date_getUTCFullYear,    0 },
    {"getMonth",          date_getMonth,          0 },
    {"getUTCMonth",       date_getUTCMonth,       0 },
    {"getDate",           date_getDate,           0 },
    {"getUTCDate",        date_getUTCDate,        0 },
    {"getDay",            date_getDay,            0 },
    {"getUTCDay",         date_getUTCDay,         0 },
    {"getHours",          date_getHours,          0 },
    {"getUTCHours",       date_getUTCHours,       0 },
    {"getMinutes",        date_getMinutes,        0 },
    {"getUTCMinutes",     date_getUTCMinutes,     0 },
    {"getSeconds",        date_getUTCSeconds,     0 },
    {"getUTCSeconds",     date_getUTCSeconds,     0 },
    {"getMilliseconds",   date_getUTCMilliseconds,0 },
    {"getUTCMilliseconds",date_getUTCMilliseconds,0 },
    {"setTime",           date_setTime,           1 },
    {"setYear",           date_setYear,           1 },
    {"setFullYear",       date_setFullYear,       3 },
    {"setUTCFullYear",    date_setUTCFullYear,    3 },
    {"setMonth",          date_setMonth,          2 },
    {"setUTCMonth",       date_setUTCMonth,       2 },
    {"setDate",           date_setDate,           1 },
    {"setUTCDate",        date_setUTCDate,        1 },
    {"setHours",          date_setHours,          4 },
    {"setUTCHours",       date_setUTCHours,       4 },
    {"setMinutes",        date_setMinutes,        3 },
    {"setUTCMinutes",     date_setUTCMinutes,     3 },
    {"setSeconds",        date_setSeconds,        2 },
    {"setUTCSeconds",     date_setUTCSeconds,     2 },
    {"setMilliseconds",   date_setMilliseconds,   1 },
    {"setUTCMilliseconds",date_setUTCMilliseconds,1 },
    {"toGMTString",       date_toGMTString,       0 },
    {"toUTCString",       date_toGMTString,       0 },
    {"toLocaleString",    date_toLocaleString,    0 },
#if JS_HAS_TOSOURCE
    {js_toSource_str,     date_toSource,          0 },
#endif
    {js_toString_str,     date_toString,          0 },
    {js_valueOf_str,      date_valueOf,           0 },
    {0}
};

static jsdouble *
date_constructor(JSContext *cx, JSObject* obj)
{
    jsdouble *date;

    date = js_NewDouble(cx, 0.0);
    if (!date)
	return NULL;
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
    return date;
}

static JSBool
Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble *date;
    JSString *str;
    jsdouble d;

    /* Date called as function */
    if (!cx->fp->constructing) {
	int64 us, ms, us2ms;
	jsdouble time;

	/* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
	 * so compute ms from PRMJ_Now.
	 */
	us = PRMJ_Now();
	JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
	JSLL_DIV(ms, us, us2ms);
	JSLL_L2D(time, ms);

	return date_format(cx, time, rval);
    }

    /* Date called as constructor */
    if (argc == 0) {
	int64 us, ms, us2ms;
	jsdouble time;

	date = date_constructor(cx, obj);
	if (!date)
	    return JS_FALSE;

	us = PRMJ_Now();
	JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
	JSLL_DIV(ms, us, us2ms);
	JSLL_L2D(time, ms);

	*date = time;
    } else if (argc == 1) {
	if (!JSVAL_IS_STRING(argv[0])) {
	    /* the argument is a millisecond number */
	    if (!js_ValueToNumber(cx, argv[0], &d))
		    return JS_FALSE;
	    date = date_constructor(cx, obj);
	    if (!date)
		return JS_FALSE;
	    *date = TIMECLIP(d);
	} else {
	    /* the argument is a string; parse it. */
	    date = date_constructor(cx, obj);
	    if (!date)
		return JS_FALSE;

	    str = js_ValueToString(cx, argv[0]);
	    if (!str)
		return JS_FALSE;

	    if (!date_parseString(str->chars, date))
		*date = *(cx->runtime->jsNaN);
	    *date = TIMECLIP(*date);
	}
    } else {
	jsdouble array[MAXARGS];
	uintN loop;
	jsdouble d;
	jsdouble day;
	jsdouble time;

	for (loop = 0; loop < MAXARGS; loop++) {
	    if (loop < argc) {
		if (!js_ValueToNumber(cx, argv[loop], &d))
		    return JS_FALSE;
		/* if any arg is NaN, make a NaN date object
		   and return */
		if (!JSDOUBLE_IS_FINITE(d)) {
		    date = date_constructor(cx, obj);
		    if (!date)
			return JS_FALSE;
		    *date = *(cx->runtime->jsNaN);
		    return JS_TRUE;
		}
		array[loop] = js_DoubleToInteger(d);
	    } else {
		array[loop] = 0;
	    }
	}

	date = date_constructor(cx, obj);
	if (!date)
	    return JS_FALSE;

	/* adjust 2-digit years into the 20th century */
	if (array[0] >= 0 && array[0] <= 99)
	    array[0] += 1900;

	/* if we got a 0 for 'date' (which is out of range)
	 * pretend it's a 1 */
	if (array[2] < 1)
	    array[2] = 1;

	day = MakeDay(array[0], array[1], array[2]);
	time = MakeTime(array[3], array[4], array[5], array[6]);
	time = MakeDate(day, time);
	time = UTC(time);
	*date = TIMECLIP(time);
    }
    return JS_TRUE;
}

JSObject *
js_InitDateClass(JSContext *cx, JSObject *obj)
{
    JSObject *proto;
    jsdouble *proto_date;

    /* set static LocalTZA */
    LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
    proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
			 NULL, date_methods, NULL, date_static_methods);
    if (!proto)
	return NULL;

    /* Set the value of the Date.prototype date to NaN */
    proto_date = date_constructor(cx, proto);
    if (!proto_date)
	return NULL;
    *proto_date = *(cx->runtime->jsNaN);

    return proto;
}

JS_FRIEND_API(JSObject *)
js_NewDateObject(JSContext* cx, int year, int mon, int mday,
		 int hour, int min, int sec)
{
    JSObject *obj;
    jsdouble *date;
    jsdouble time;

    obj = js_NewObject(cx, &date_class, NULL, NULL);
    if (!obj)
	return NULL;

    JS_DefineFunctions(cx, obj, date_methods);

    date = date_constructor(cx, obj);
    if (!date)
	return NULL;

    time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
    *date = UTC(time);
    return obj;
}

JS_FRIEND_API(int)
js_DateGetYear(JSContext *cx, JSObject* obj)
{
    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)
	return 0;
    return (int) YearFromTime(LocalTime(*date));
}

JS_FRIEND_API(int)
js_DateGetMonth(JSContext *cx, JSObject* obj)
{
    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)
	return 0;
    return (int) MonthFromTime(LocalTime(*date));
}

JS_FRIEND_API(int)
js_DateGetDate(JSContext *cx, JSObject* obj)
{
    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)
	return 0;
    return (int) DateFromTime(LocalTime(*date));
}

JS_FRIEND_API(int)
js_DateGetHours(JSContext *cx, JSObject* obj)
{
    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)
	return 0;
    return (int) HourFromTime(LocalTime(*date));
}

JS_FRIEND_API(int)
js_DateGetMinutes(JSContext *cx, JSObject* obj)
{
    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)
	return 0;
    return (int) MinFromTime(LocalTime(*date));
}

JS_FRIEND_API(int)
js_DateGetSeconds(JSContext *cx, JSObject* obj)
{
    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)
	return 0;
    return (int) SecFromTime(*date);
}

extern JS_FRIEND_API(void)
js_DateSetYear(JSContext *cx, JSObject *obj, int year)
{
    jsdouble local;
    jsdouble *date = date_getProlog(cx, obj, NULL);
    if (!date)
	return;
    local = LocalTime(*date);
    /* reset date if it was NaN */
    if (JSDOUBLE_IS_NaN(local))
	local = 0;
    local = date_msecFromDate(year,
			      MonthFromTime(local),
			      DateFromTime(local),
			      HourFromTime(local),
			      MinFromTime(local),
			      SecFromTime(local),
			      msFromTime(local));
    *date = UTC(local);
}

extern JS_FRIEND_API(void)
js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
{
    jsdouble local;
    jsdouble *date = date_getProlog(cx, obj, NULL);
    if (!date)
	return;
    local = LocalTime(*date);
    /* bail if date was NaN */
    if (JSDOUBLE_IS_NaN(local))
	return;
    local = date_msecFromDate(YearFromTime(local),
			      month,
			      DateFromTime(local),
			      HourFromTime(local),
			      MinFromTime(local),
			      SecFromTime(local),
			      msFromTime(local));
    *date = UTC(local);
}

extern JS_FRIEND_API(void)
js_DateSetDate(JSContext *cx, JSObject *obj, int date)
{
    jsdouble local;
    jsdouble *datep = date_getProlog(cx, obj, NULL);
    if (!datep)
	return;
    local = LocalTime(*datep);
    if (JSDOUBLE_IS_NaN(local))
	return;
    local = date_msecFromDate(YearFromTime(local),
			      MonthFromTime(local),
			      date,
			      HourFromTime(local),
			      MinFromTime(local),
			      SecFromTime(local),
			      msFromTime(local));
    *datep = UTC(local);
}

extern JS_FRIEND_API(void)
js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
{
    jsdouble local;
    jsdouble *date = date_getProlog(cx, obj, NULL);
    if (!date)
	return;
    local = LocalTime(*date);
    if (JSDOUBLE_IS_NaN(local))
	return;
    local = date_msecFromDate(YearFromTime(local),
			      MonthFromTime(local),
			      DateFromTime(local),
			      hours,
			      MinFromTime(local),
			      SecFromTime(local),
			      msFromTime(local));
    *date = UTC(local);
}

extern JS_FRIEND_API(void)
js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
{
    jsdouble local;
    jsdouble *date = date_getProlog(cx, obj, NULL);
    if (!date)
	return;
    local = LocalTime(*date);
    if (JSDOUBLE_IS_NaN(local))
	return;
    local = date_msecFromDate(YearFromTime(local),
			      MonthFromTime(local),
			      DateFromTime(local),
			      HourFromTime(local),
			      minutes,
			      SecFromTime(local),
			      msFromTime(local));
    *date = UTC(local);
}

extern JS_FRIEND_API(void)
js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
{
    jsdouble local;
    jsdouble *date = date_getProlog(cx, obj, NULL);
    if (!date)
	return;
    local = LocalTime(*date);
    if (JSDOUBLE_IS_NaN(local))
	return;
    local = date_msecFromDate(YearFromTime(local),
			      MonthFromTime(local),
			      DateFromTime(local),
			      HourFromTime(local),
			      MinFromTime(local),
			      seconds,
			      msFromTime(local));
    *date = UTC(local);
}

**** End of jsdate.c. ****

**** Start of jsdate.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsdate_h___
#define jsdate_h___
/*
 * JS Date class interface.
 */

JS_BEGIN_EXTERN_C

extern JSObject *
js_InitDateClass(JSContext *cx, JSObject *obj);

/*
 *  These functions provide a C interface to the date/time object
 */
extern JS_FRIEND_API(JSObject*)
js_NewDateObject(JSContext* cx, int year, int mon, int mday,
				int hour, int min, int sec);

extern JS_FRIEND_API(int)
js_DateGetYear(JSContext *cx, JSObject* obj);

extern JS_FRIEND_API(int)
js_DateGetMonth(JSContext *cx, JSObject* obj);

extern JS_FRIEND_API(int)
js_DateGetDate(JSContext *cx, JSObject* obj);

extern JS_FRIEND_API(int)
js_DateGetHours(JSContext *cx, JSObject* obj);

extern JS_FRIEND_API(int)
js_DateGetMinutes(JSContext *cx, JSObject* obj);

extern JS_FRIEND_API(int)
js_DateGetSeconds(JSContext *cx, JSObject* obj);

extern JS_FRIEND_API(void)
js_DateSetYear(JSContext *cx, JSObject *obj, int year);

extern JS_FRIEND_API(void)
js_DateSetMonth(JSContext *cx, JSObject *obj, int year);

extern JS_FRIEND_API(void)
js_DateSetDate(JSContext *cx, JSObject *obj, int date);

extern JS_FRIEND_API(void)
js_DateSetHours(JSContext *cx, JSObject *obj, int hours);

extern JS_FRIEND_API(void)
js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes);

extern JS_FRIEND_API(void)
js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds);


JS_END_EXTERN_C

#endif /* jsdate_h___ */

**** End of jsdate.h. ****

**** Start of jsdbgapi.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS debugging API.
 */
#include "jsstddef.h"
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsclist.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

typedef struct JSTrap {
    JSCList         links;
    JSScript        *script;
    jsbytecode      *pc;
    JSOp            op;
    JSTrapHandler   handler;
    void            *closure;
} JSTrap;

static JSTrap *
FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
{
    JSTrap *trap;

    for (trap = (JSTrap *)rt->trapList.next;
	 trap != (JSTrap *)&rt->trapList;
	 trap = (JSTrap *)trap->links.next) {
	if (trap->script == script && trap->pc == pc)
	    return trap;
    }
    return NULL;
}

void
js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
{
    JSTrap *trap;

    trap = FindTrap(cx->runtime, script, pc);
    if (trap)
	trap->op = op;
    else
	*pc = (jsbytecode)op;
}

JS_PUBLIC_API(JSBool)
JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
	   JSTrapHandler handler, void *closure)
{
    JSRuntime *rt;
    JSTrap *trap;

    rt = cx->runtime;
    trap = FindTrap(rt, script, pc);
    if (trap) {
	/* Restore opcode at pc so it can be saved again. */
	*pc = (jsbytecode)trap->op;
    } else {
	trap = JS_malloc(cx, sizeof *trap);
	if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) {
	    if (trap)
		JS_free(cx, trap);
	    return JS_FALSE;
	}
    }
    JS_APPEND_LINK(&trap->links, &rt->trapList);
    trap->script = script;
    trap->pc = pc;
    trap->op = (JSOp)*pc;
    trap->handler = handler;
    trap->closure = closure;
    *pc = JSOP_TRAP;
    return JS_TRUE;
}

JS_PUBLIC_API(JSOp)
JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
{
    JSTrap *trap;

    trap = FindTrap(cx->runtime, script, pc);
    if (!trap) {
	JS_ASSERT(0);	/* XXX can't happen */
	return JSOP_LIMIT;
    }
    return trap->op;
}

static void
DestroyTrap(JSContext *cx, JSTrap *trap)
{
    JS_REMOVE_LINK(&trap->links);
    *trap->pc = (jsbytecode)trap->op;
    js_RemoveRoot(cx, &trap->closure);
    JS_free(cx, trap);
}

JS_PUBLIC_API(void)
JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
	     JSTrapHandler *handlerp, void **closurep)
{
    JSTrap *trap;

    trap = FindTrap(cx->runtime, script, pc);
    if (handlerp)
	*handlerp = trap ? trap->handler : NULL;
    if (closurep)
	*closurep = trap ? trap->closure : NULL;
    if (trap)
	DestroyTrap(cx, trap);
}

JS_PUBLIC_API(void)
JS_ClearScriptTraps(JSContext *cx, JSScript *script)
{
    JSRuntime *rt;
    JSTrap *trap, *next;

    rt = cx->runtime;
    for (trap = (JSTrap *)rt->trapList.next;
	 trap != (JSTrap *)&rt->trapList;
	 trap = next) {
	next = (JSTrap *)trap->links.next;
	if (trap->script == script)
	    DestroyTrap(cx, trap);
    }
}

JS_PUBLIC_API(void)
JS_ClearAllTraps(JSContext *cx)
{
    JSRuntime *rt;
    JSTrap *trap, *next;

    rt = cx->runtime;
    for (trap = (JSTrap *)rt->trapList.next;
	 trap != (JSTrap *)&rt->trapList;
	 trap = next) {
	next = (JSTrap *)trap->links.next;
	DestroyTrap(cx, trap);
    }
}

JS_PUBLIC_API(JSTrapStatus)
JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
{
    JSTrap *trap;
    JSTrapStatus status;
    jsint op;

    trap = FindTrap(cx->runtime, script, pc);
    if (!trap) {
	JS_ASSERT(0);	/* XXX can't happen */
	return JSTRAP_ERROR;
    }
    /*
     * It's important that we not use 'trap->' after calling the callback --
     * the callback might remove the trap!
     */
    op = (jsint)trap->op;
    status = trap->handler(cx, script, pc, rval, trap->closure);
    if (status == JSTRAP_CONTINUE) {
	/* By convention, return the true op to the interpreter in rval. */
	*rval = INT_TO_JSVAL(op);
    }
    return status;
}

JS_PUBLIC_API(JSBool)
JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
{
    rt->interruptHandler = handler;
    rt->interruptHandlerData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
{
    if (handlerp)
	*handlerp = (JSTrapHandler)rt->interruptHandler;
    if (closurep)
	*closurep = rt->interruptHandlerData;
    rt->interruptHandler = 0;
    rt->interruptHandlerData = 0;
    return JS_TRUE;
}


typedef struct JSWatchPoint {
    JSCList             links;
    JSObject            *object;	/* weak link, see js_FinalizeObject */
    jsval               userid;
    JSScopeProperty     *sprop;
    JSPropertyOp        setter;
    JSWatchPointHandler handler;
    void                *closure;
    jsrefcount          nrefs;
} JSWatchPoint;

#define HoldWatchPoint(wp) ((wp)->nrefs++)

static void
DropWatchPoint(JSContext *cx, JSWatchPoint *wp)
{
    if (--wp->nrefs != 0)
	return;
    wp->sprop->setter = wp->setter;
    JS_LOCK_OBJ_VOID(cx, wp->object,
		     js_DropScopeProperty(cx, (JSScope *)wp->object->map,
					  wp->sprop));
    JS_REMOVE_LINK(&wp->links);
    js_RemoveRoot(cx, &wp->closure);
    JS_free(cx, wp);
}

static JSWatchPoint *
FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid)
{
    JSWatchPoint *wp;

    for (wp = (JSWatchPoint *)rt->watchPointList.next;
	 wp != (JSWatchPoint *)&rt->watchPointList;
	 wp = (JSWatchPoint *)wp->links.next) {
	if (wp->object == obj && wp->userid == userid)
	    return wp;
    }
    return NULL;
}

JSScopeProperty *
js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid)
{
    JSWatchPoint *wp;

    wp = FindWatchPoint(rt, obj, userid);
    if (!wp)
	return NULL;
    return wp->sprop;
}

JSBool JS_DLL_CALLBACK
js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSRuntime *rt;
    JSWatchPoint *wp;
    JSScopeProperty *sprop;
    JSSymbol *sym;
    jsval userid, value;
    jsid symid;
    JSScope *scope;
    JSAtom *atom;
    JSBool ok;

    rt = cx->runtime;
    for (wp = (JSWatchPoint *)rt->watchPointList.next;
	 wp != (JSWatchPoint *)&rt->watchPointList;
	 wp = (JSWatchPoint *)wp->links.next) {
	sprop = wp->sprop;
	if (wp->object == obj && sprop->id == id) {
	    JS_LOCK_OBJ(cx, obj);
	    sym = sprop->symbols;
	    if (!sym) {
		userid = wp->userid;
		atom = NULL;
		if (JSVAL_IS_INT(userid)) {
		    symid = (jsid)userid;
		} else {
		    atom = js_ValueToStringAtom(cx, userid);
		    if (!atom) {
			JS_UNLOCK_OBJ(cx, obj);
			return JS_FALSE;
		    }
		    symid = (jsid)atom;
		}
		scope = (JSScope *) obj->map;
		JS_ASSERT(scope->props);
		ok = LOCKED_OBJ_GET_CLASS(obj)->addProperty(cx, obj, sprop->id,
							    &value);
		if (!ok) {
		    JS_UNLOCK_OBJ(cx, obj);
		    return JS_FALSE;
		}
		ok = (scope->ops->add(cx, scope, symid, sprop) != NULL);
		if (!ok) {
		    JS_UNLOCK_OBJ(cx, obj);
		    return JS_FALSE;
		}
		sym = sprop->symbols;
	    }
	    JS_UNLOCK_OBJ(cx, obj);
	    HoldWatchPoint(wp);
	    ok = wp->handler(cx, obj, js_IdToValue(sym_id(sym)),
			     OBJ_GET_SLOT(cx, obj, wp->sprop->slot), vp,
			     wp->closure);
	    if (ok)
		ok = wp->setter(cx, obj, id, vp);
	    DropWatchPoint(cx, wp);
	    return ok;
	}
    }
    JS_ASSERT(0);	/* XXX can't happen */
    return JS_FALSE;
}

JS_PUBLIC_API(JSBool)
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
		 JSWatchPointHandler handler, void *closure)
{
    JSAtom *atom;
    jsid symid;
    JSObject *pobj;
    JSScopeProperty *sprop;
    JSRuntime *rt;
    JSWatchPoint *wp;

    if (!OBJ_IS_NATIVE(obj)) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
			     OBJ_GET_CLASS(cx, obj)->name);
	return JS_FALSE;
    }

    if (JSVAL_IS_INT(id)) {
	symid = (jsid)id;
	atom = NULL;
    } else {
	atom = js_ValueToStringAtom(cx, id);
	if (!atom)
	    return JS_FALSE;
	symid = (jsid)atom;
    }

    if (!js_LookupProperty(cx, obj, symid, &pobj, (JSProperty **)&sprop))
	return JS_FALSE;
    rt = cx->runtime;
    if (!sprop) {
	/* Check for a deleted symbol watchpoint, which holds its property. */
	sprop = js_FindWatchPoint(rt, obj, id);
	if (sprop) {
#ifdef JS_THREADSAFE
	    /* Emulate js_LookupProperty if thread-safe. */
	    JS_LOCK_OBJ(cx, obj);
	    sprop->nrefs++;
#endif
	} else {
	    /* Make a new property in obj so we can watch for the first set. */
	    if (!js_DefineProperty(cx, obj, symid, JSVAL_VOID, NULL, NULL, 0,
				   (JSProperty **)&sprop)) {
		sprop = NULL;
	    }
	}
    } else if (pobj != obj) {
	/* Clone the prototype property so we can watch the right object. */
	jsval value;
	JSPropertyOp getter, setter;
	uintN attrs;

	if (OBJ_IS_NATIVE(pobj)) {
	    value = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
	} else {
	    if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) {
		OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
		return JS_FALSE;
	    }
	}
	getter = sprop->getter;
	setter = sprop->setter;
	attrs = sprop->attrs;
	OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

	if (!js_DefineProperty(cx, obj, symid, value, getter, setter, attrs,
			       (JSProperty **)&sprop)) {
	    sprop = NULL;
	}
    }
    if (!sprop)
	return JS_FALSE;

    wp = FindWatchPoint(rt, obj, id);
    if (!wp) {
	wp = JS_malloc(cx, sizeof *wp);
	if (!wp)
	    return JS_FALSE;
	if (!js_AddRoot(cx, &wp->closure, "wp->closure")) {
	    JS_free(cx, wp);
	    return JS_FALSE;
	}
	JS_APPEND_LINK(&wp->links, &rt->watchPointList);
	wp->object = obj;
	wp->userid = id;
	wp->sprop = js_HoldScopeProperty(cx, (JSScope *)obj->map, sprop);
	wp->setter = sprop->setter;
	sprop->setter = js_watch_set;
	wp->nrefs = 1;
    }
    wp->handler = handler;
    wp->closure = closure;
    OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);
    return JS_TRUE;
}

JS_PUBLIC_API(void)
JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
		   JSWatchPointHandler *handlerp, void **closurep)
{
    JSRuntime *rt;
    JSWatchPoint *wp;

    rt = cx->runtime;
    for (wp = (JSWatchPoint *)rt->watchPointList.next;
	 wp != (JSWatchPoint *)&rt->watchPointList;
	 wp = (JSWatchPoint *)wp->links.next) {
	if (wp->object == obj && wp->userid == id) {
	    if (handlerp)
		*handlerp = wp->handler;
	    if (closurep)
		*closurep = wp->closure;
	    DropWatchPoint(cx, wp);
	    return;
	}
    }
    if (handlerp)
	*handlerp = NULL;
    if (closurep)
	*closurep = NULL;
}

JS_PUBLIC_API(void)
JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
{
    JSRuntime *rt;
    JSWatchPoint *wp, *next;

    rt = cx->runtime;
    for (wp = (JSWatchPoint *)rt->watchPointList.next;
	 wp != (JSWatchPoint *)&rt->watchPointList;
	 wp = next) {
	next = (JSWatchPoint *)wp->links.next;
	if (wp->object == obj)
	    DropWatchPoint(cx, wp);
    }
}

JS_PUBLIC_API(void)
JS_ClearAllWatchPoints(JSContext *cx)
{
    JSRuntime *rt;
    JSWatchPoint *wp, *next;

    rt = cx->runtime;
    for (wp = (JSWatchPoint *)rt->watchPointList.next;
	 wp != (JSWatchPoint *)&rt->watchPointList;
	 wp = next) {
	next = (JSWatchPoint *)wp->links.next;
	DropWatchPoint(cx, wp);
    }
}

JS_PUBLIC_API(uintN)
JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
{
    return js_PCToLineNumber(script, pc);
}

JS_PUBLIC_API(jsbytecode *)
JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
{
    return js_LineNumberToPC(script, lineno);
}

JS_PUBLIC_API(JSScript *)
JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
{
    return fun->script;
}

JS_PUBLIC_API(JSPrincipals *)
JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
{
    return script->principals;
}


/*
 *  Stack Frame Iterator
 */
JS_PUBLIC_API(JSStackFrame *)
JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
{
    *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down;
    return *iteratorp;
}

JS_PUBLIC_API(JSScript *)
JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
{
    return fp->script;
}

JS_PUBLIC_API(jsbytecode *)
JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
{
    return fp->pc;
}

JS_PUBLIC_API(void *)
JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
{
    if (fp->annotation) {
	JSPrincipals *principals = fp->script
	    ? fp->script->principals
	    : NULL;

	if (principals == NULL)
	    return NULL;

	if (principals->globalPrivilegesEnabled(cx, principals)) {
	    /*
	     * Only give out an annotation if privileges have not
	     * been revoked globally.
	     */
	    return fp->annotation;
	}
    }

    return NULL;
}

JS_PUBLIC_API(void)
JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
{
    fp->annotation = annotation;
}

JS_PUBLIC_API(void *)
JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
{
    JSPrincipals *principals = fp->script
	? fp->script->principals
	: NULL;

    return principals
	? principals->getPrincipalArray(cx, principals)
	: NULL;
}

JS_PUBLIC_API(JSBool)
JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
{
    return fp->fun && fp->fun->call;
}

/* this is deprecated, use JS_GetFrameScopeChain instead */
JS_PUBLIC_API(JSObject *)
JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
{
    return fp->scopeChain;
}

JS_PUBLIC_API(JSObject *)
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
{
    /* Force creation of argument and call objects if not yet created */
    JS_GetFrameCallObject(cx, fp);
    return fp->scopeChain;
}

JS_PUBLIC_API(JSObject *)
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
{
    if (! fp->fun)
	return NULL;
    /* Force creation of argument object if not yet created */
     js_GetArgsObject(cx, fp);
#if JS_HAS_CALL_OBJECT
    return js_GetCallObject(cx, fp, NULL, NULL);
#else
    return NULL;
#endif /* JS_HAS_CALL_OBJECT */
}


JS_PUBLIC_API(JSObject *)
JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
{
    return fp->thisp;
}

JS_PUBLIC_API(JSFunction *)
JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
{
    return fp->fun;
}

JS_PUBLIC_API(JSBool)
JS_IsContructorFrame(JSContext *cx, JSStackFrame *fp)
{
    return fp->constructing;
}        

JS_PUBLIC_API(JSBool)
JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
{
    return fp->debugging;
}        

JS_PUBLIC_API(jsval)
JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
{
    return fp->rval;
}        

JS_PUBLIC_API(void)
JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
{
    fp->rval = rval;
}        

/************************************************************************/

JS_PUBLIC_API(const char *)
JS_GetScriptFilename(JSContext *cx, JSScript *script)
{
    return script->filename;
}

JS_PUBLIC_API(uintN)
JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
{
    return script->lineno;
}

JS_PUBLIC_API(uintN)
JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
{
    return js_GetScriptLineExtent(script);
}

/***************************************************************************/

JS_PUBLIC_API(void)
JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
{
    rt->newScriptHook = hook;
    rt->newScriptHookData = callerdata;
}

JS_PUBLIC_API(void)
JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
			void *callerdata)
{
    rt->destroyScriptHook = hook;
    rt->destroyScriptHookData = callerdata;
}

/***************************************************************************/

JS_PUBLIC_API(JSBool)
JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
			const char *bytes, uintN length,
			const char *filename, uintN lineno,
			jsval *rval)
{
    JSScript *script;
    JSBool ok;

    script = JS_CompileScriptForPrincipals(cx, fp->scopeChain,
					   fp->script ? fp->script->principals
						      : NULL,
					   bytes, length, filename, lineno);
    if (!script)
	return JS_FALSE;
    ok = js_Execute(cx, fp->scopeChain, script, fp->fun, fp, JS_TRUE, rval);
    js_DestroyScript(cx, script);
    return ok;
}

/************************************************************************/

JS_PUBLIC_API(JSScopeProperty *)
JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
{
    JSScopeProperty *sprop;
    JSScope *scope;

    sprop = *iteratorp;
    scope = (JSScope *) obj->map;
    sprop = (sprop == NULL) ? scope->props : sprop->next;
    *iteratorp = sprop;
    return sprop;
}

JS_PUBLIC_API(JSBool)
JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
		   JSPropertyDesc *pd)
{
    JSSymbol *sym;

    sym = sprop->symbols;
    pd->id = sym ? js_IdToValue(sym_id(sym)) : JSVAL_VOID;
    if (!sym || !js_GetProperty(cx, obj, sym_id(sym), &pd->value))
	pd->value = OBJ_GET_SLOT(cx, obj, sprop->slot);
    pd->flags = ((sprop->attrs & JSPROP_ENUMERATE)      ? JSPD_ENUMERATE : 0)
	      | ((sprop->attrs & JSPROP_READONLY)       ? JSPD_READONLY  : 0)
	      | ((sprop->attrs & JSPROP_PERMANENT)      ? JSPD_PERMANENT : 0)
#if JS_HAS_CALL_OBJECT
	      | ((sprop->getter == js_GetCallVariable)  ? JSPD_VARIABLE  : 0)
#endif /* JS_HAS_CALL_OBJECT */
	      | ((sprop->getter == js_GetArgument)      ? JSPD_ARGUMENT  : 0)
	      | ((sprop->getter == js_GetLocalVariable) ? JSPD_VARIABLE  : 0);
#if JS_HAS_CALL_OBJECT
    /* for Call Object 'real' getter isn't passed in to us */
    if (OBJ_GET_CLASS(cx, obj) == &js_CallClass &&
	OBJ_GET_CLASS(cx, obj)->getProperty == sprop->getter)
	pd->flags |= JSPD_ARGUMENT;
#endif /* JS_HAS_CALL_OBJECT */
    pd->spare = 0;
    pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))
	       ? JSVAL_TO_INT(sprop->id)
	       : 0;
    if (!sym || !sym->next || (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))) {
	pd->alias = JSVAL_VOID;
    } else {
	pd->alias = js_IdToValue(sym_id(sym->next));
	pd->flags |= JSPD_ALIAS;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
{
    JSScope *scope;
    uint32 i, n;
    JSPropertyDesc *pd;
    JSScopeProperty *sprop;
    jsval state;
    jsid  num_prop;

    if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, &num_prop))
	return JS_FALSE;
    scope = (JSScope *)obj->map;
    /* have no props, or object's scope has not mutated from that of proto */
    if (!scope->props ||
	(OBJ_GET_PROTO(cx,obj) &&
	 scope == (JSScope *)(OBJ_GET_PROTO(cx,obj)->map))) {
	pda->length = 0;
	pda->array = NULL;
	return JS_TRUE;
    }
    n = scope->map.freeslot;
    pd = JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));
    if (!pd)
	return JS_FALSE;
    i = 0;
    for (sprop = scope->props; sprop; sprop = sprop->next) {
	if (!js_AddRoot(cx, &pd[i].id, NULL))
	    goto bad;
	if (!js_AddRoot(cx, &pd[i].value, NULL))
	    goto bad;
	JS_GetPropertyDesc(cx, obj, sprop, &pd[i]);
	if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
	    goto bad;
	if (++i == n)
	    break;
    }
    pda->length = i;
    pda->array = pd;
    return JS_TRUE;

bad:
    pda->length = i + 1;
    pda->array = pd;
    JS_PutPropertyDescArray(cx, pda);
    return JS_FALSE;
}

JS_PUBLIC_API(void)
JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
{
    JSPropertyDesc *pd;
    uint32 i;

    pd = pda->array;
    for (i = 0; i < pda->length; i++) {
	js_RemoveRoot(cx, &pd[i].id);
	js_RemoveRoot(cx, &pd[i].value);
	if (pd[i].flags & JSPD_ALIAS)
	    js_RemoveRoot(cx, &pd[i].alias);
    }
    JS_free(cx, pd);
}

/************************************************************************/

JS_PUBLIC_API(JSBool)
JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
{
    rt->debuggerHandler = handler;
    rt->debuggerHandlerData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
{
    rt->sourceHandler = handler;
    rt->sourceHandlerData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
{
    rt->executeHook = hook;
    rt->executeHookData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
{
    rt->callHook = hook;
    rt->callHookData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
{
    rt->objectHook = hook;
    rt->objectHookData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
{
    rt->throwHook = hook;
    rt->throwHookData = closure;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
{
    rt->debugErrorHook = hook;
    rt->debugErrorHookData = closure;
    return JS_TRUE;
}

**** End of jsdbgapi.c. ****

**** Start of jsdbgapi.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsdbgapi_h___
#define jsdbgapi_h___
/*
 * JS debugger API.
 */
#include "jsapi.h"
#include "jsopcode.h"
#include "jsprvtd.h"

JS_BEGIN_EXTERN_C

extern void
js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op);

extern JS_PUBLIC_API(JSBool)
JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
	   JSTrapHandler handler, void *closure);

extern JS_PUBLIC_API(JSOp)
JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc);

extern JS_PUBLIC_API(void)
JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
	     JSTrapHandler *handlerp, void **closurep);

extern JS_PUBLIC_API(void)
JS_ClearScriptTraps(JSContext *cx, JSScript *script);

extern JS_PUBLIC_API(void)
JS_ClearAllTraps(JSContext *cx);

extern JS_PUBLIC_API(JSTrapStatus)
JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval);

extern JS_PUBLIC_API(JSBool)
JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep);

/************************************************************************/

extern JS_PUBLIC_API(JSBool)
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
		 JSWatchPointHandler handler, void *closure);

extern JS_PUBLIC_API(void)
JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
		   JSWatchPointHandler *handlerp, void **closurep);

extern JS_PUBLIC_API(void)
JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj);

extern JS_PUBLIC_API(void)
JS_ClearAllWatchPoints(JSContext *cx);

#ifdef JS_HAS_OBJ_WATCHPOINT
/*
 * Hide these non-API function prototypes by testing whether the internal
 * header file "jsconfig.h" has been included.
 */
extern JSScopeProperty *
js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid);

extern JSBool JS_DLL_CALLBACK
js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
#endif

/************************************************************************/

extern JS_PUBLIC_API(uintN)
JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);

extern JS_PUBLIC_API(jsbytecode *)
JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno);

extern JS_PUBLIC_API(JSScript *)
JS_GetFunctionScript(JSContext *cx, JSFunction *fun);

extern JS_PUBLIC_API(JSPrincipals *)
JS_GetScriptPrincipals(JSContext *cx, JSScript *script);

/*
 * Stack Frame Iterator
 *
 * Used to iterate through the JS stack frames to extract
 * information from the frames.
 */

extern JS_PUBLIC_API(JSStackFrame *)
JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp);

extern JS_PUBLIC_API(JSScript *)
JS_GetFrameScript(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(jsbytecode *)
JS_GetFramePC(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSBool)
JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(void *)
JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(void)
JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation);

extern JS_PUBLIC_API(void *)
JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp);

/* this is deprecated, use JS_GetFrameScopeChain instead */
extern JS_PUBLIC_API(JSObject *)
JS_GetFrameObject(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSObject *)
JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSObject *)
JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSObject *)
JS_GetFrameThis(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSFunction *)
JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSBool)
JS_IsContructorFrame(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(JSBool)
JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(jsval)
JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp);

extern JS_PUBLIC_API(void)
JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval);

/************************************************************************/

extern JS_PUBLIC_API(const char *)
JS_GetScriptFilename(JSContext *cx, JSScript *script);

extern JS_PUBLIC_API(uintN)
JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script);

extern JS_PUBLIC_API(uintN)
JS_GetScriptLineExtent(JSContext *cx, JSScript *script);

/************************************************************************/

/*
 * Hook setters for script creation and destruction, see jsprvtd.h for the
 * typedefs.  These macros provide binary compatibility and newer, shorter
 * synonyms.
 */
#define JS_SetNewScriptHook     JS_SetNewScriptHookProc
#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc

extern JS_PUBLIC_API(void)
JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata);

extern JS_PUBLIC_API(void)
JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
			void *callerdata);

/************************************************************************/

extern JS_PUBLIC_API(JSBool)
JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
			const char *bytes, uintN length,
			const char *filename, uintN lineno,
			jsval *rval);

/************************************************************************/

typedef struct JSPropertyDesc {
    jsval           id;         /* primary id, a string or int */
    jsval           value;      /* property value */
    uint8           flags;      /* flags, see below */
    uint8           spare;      /* unused */
    uint16          slot;       /* argument/variable slot */
    jsval           alias;      /* alias id if JSPD_ALIAS flag */
} JSPropertyDesc;

#define JSPD_ENUMERATE  0x01    /* visible to for/in loop */
#define JSPD_READONLY   0x02    /* assignment is error */
#define JSPD_PERMANENT  0x04    /* property cannot be deleted */
#define JSPD_ALIAS      0x08    /* property has an alias id */
#define JSPD_ARGUMENT   0x10    /* argument to function */
#define JSPD_VARIABLE   0x20    /* local variable in function */

typedef struct JSPropertyDescArray {
    uint32          length;     /* number of elements in array */
    JSPropertyDesc  *array;     /* alloc'd by Get, freed by Put */
} JSPropertyDescArray;

extern JS_PUBLIC_API(JSScopeProperty *)
JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp);

extern JS_PUBLIC_API(JSBool)
JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
		   JSPropertyDesc *pd);

extern JS_PUBLIC_API(JSBool)
JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda);

extern JS_PUBLIC_API(void)
JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda);

/************************************************************************/

extern JS_PUBLIC_API(JSBool)
JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure);

extern JS_PUBLIC_API(JSBool)
JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure);

JS_END_EXTERN_C

#endif /* jsdbgapi_h___ */

**** End of jsdbgapi.h. ****

**** Start of jsdtoa.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * Portable double to alphanumeric string and back converters.
 */
#include "jsstddef.h"
#include "jstypes.h"
#include "jsdtoa.h"
#include "jsprf.h"
#include "jsutil.h" /* Added by JSIFY */

#ifdef JS_THREADSAFE
#include "prlock.h"
#endif

/****************************************************************
 *
 * The author of this software is David M. Gay.
 *
 * Copyright (c) 1991 by AT&T.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 *
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 *
 ***************************************************************/

/* Please send bug reports to
	David M. Gay
	AT&T Bell Laboratories, Room 2C-463
	600 Mountain Avenue
	Murray Hill, NJ 07974-2070
	U.S.A.
	dmg@research.att.com or research!dmg
 */

/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
 *
 * This strtod returns a nearest machine number to the input decimal
 * string (or sets errno to ERANGE).  With IEEE arithmetic, ties are
 * broken by the IEEE round-even rule.  Otherwise ties are broken by
 * biased rounding (add half and chop).
 *
 * Inspired loosely by William D. Clinger's paper "How to Read Floating
 * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
 *
 * Modifications:
 *
 *	1. We only require IEEE, IBM, or VAX double-precision
 *		arithmetic (not IEEE double-extended).
 *	2. We get by with floating-point arithmetic in a case that
 *		Clinger missed -- when we're computing d * 10^n
 *		for a small integer d and the integer n is not too
 *		much larger than 22 (the maximum integer k for which
 *		we can represent 10^k exactly), we may be able to
 *		compute (d*10^k) * 10^(e-k) with just one roundoff.
 *	3. Rather than a bit-at-a-time adjustment of the binary
 *		result in the hard case, we use floating-point
 *		arithmetic to determine the adjustment to within
 *		one bit; only in really hard cases do we need to
 *		compute a second residual.
 *	4. Because of 3., we don't need a large table of powers of 10
 *		for ten-to-e (just some small tables, e.g. of 10^k
 *		for 0 <= k <= 22).
 */

/*
 * #define IEEE_8087 for IEEE-arithmetic machines where the least
 *	significant byte has the lowest address.
 * #define IEEE_MC68k for IEEE-arithmetic machines where the most
 *	significant byte has the lowest address.
 * #define Long int on machines with 32-bit ints and 64-bit longs.
 * #define Sudden_Underflow for IEEE-format machines without gradual
 *	underflow (i.e., that flush to zero on underflow).
 * #define IBM for IBM mainframe-style floating-point arithmetic.
 * #define VAX for VAX-style floating-point arithmetic.
 * #define Unsigned_Shifts if >> does treats its left operand as unsigned.
 * #define No_leftright to omit left-right logic in fast floating-point
 *	computation of JS_dtoa.
 * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3.
 * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
 *	that use extended-precision instructions to compute rounded
 *	products and quotients) with IBM.
 * #define ROUND_BIASED for IEEE-format with biased rounding.
 * #define Inaccurate_Divide for IEEE-format with correctly rounded
 *	products but inaccurate quotients, e.g., for Intel i860.
 * #define Just_16 to store 16 bits per 32-bit Long when doing high-precision
 *	integer arithmetic.  Whether this speeds things up or slows things
 *	down depends on the machine and the number being converted.
 * #define KR_headers for old-style C function headers.
 * #define Bad_float_h if your system lacks a float.h or if it does not
 *	define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
 *	FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
 * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
 *	if memory is available and otherwise does something you deem
 *	appropriate.  If MALLOC is undefined, malloc will be invoked
 *	directly -- and assumed always to succeed.
 */
#ifdef IS_LITTLE_ENDIAN
#define IEEE_8087
#else
#define IEEE_MC68k
#endif

#ifndef Long
#define Long int32
#endif

#ifndef ULong
#define ULong uint32
#endif

#ifdef DEBUG_rrj
#include "stdio.h"
#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
#else
#define Bug(x) (void)0
#endif

#include "stdlib.h"
#include "string.h"

#ifdef MALLOC
extern void *MALLOC(size_t);
#else
#define MALLOC malloc
#endif

#include "errno.h"
#ifdef Bad_float_h
#undef __STDC__
#ifdef IEEE_MC68k
#define IEEE_ARITHMETIC
#endif
#ifdef IEEE_8087
#define IEEE_ARITHMETIC
#endif

#ifdef IEEE_ARITHMETIC
#define DBL_DIG 15
#define DBL_MAX_10_EXP 308
#define DBL_MAX_EXP 1024
#define FLT_RADIX 2
#define FLT_ROUNDS 1
#define DBL_MAX 1.7976931348623157e+308
#endif

#ifdef IBM
#define DBL_DIG 16
#define DBL_MAX_10_EXP 75
#define DBL_MAX_EXP 63
#define FLT_RADIX 16
#define FLT_ROUNDS 0
#define DBL_MAX 7.2370055773322621e+75
#endif

#ifdef VAX
#define DBL_DIG 16
#define DBL_MAX_10_EXP 38
#define DBL_MAX_EXP 127
#define FLT_RADIX 2
#define FLT_ROUNDS 1
#define DBL_MAX 1.7014118346046923e+38
#endif

#ifndef LONG_MAX
#define LONG_MAX 2147483647
#endif
#else
#include "float.h"
#endif
#ifndef __MATH_H__
#include "math.h"
#endif

#ifndef CONST
#define CONST const
#endif

#ifdef Unsigned_Shifts
#define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000;
#else
#define Sign_Extend(a,b) /*no-op*/
#endif

#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1
Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.
#endif

#ifdef IEEE_8087
#define word0(x) ((ULong *)&x)[1]
#define word1(x) ((ULong *)&x)[0]
#else
#define word0(x) ((ULong *)&x)[0]
#define word1(x) ((ULong *)&x)[1]
#endif

/* The following definition of Storeinc is appropriate for MIPS processors.
 * An alternative that might be better on some machines is
 * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
 */
#if defined(IEEE_8087) + defined(VAX)
#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
((unsigned short *)a)[0] = (unsigned short)c, a++)
#else
#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
((unsigned short *)a)[1] = (unsigned short)c, a++)
#endif

/* #define P DBL_MANT_DIG */
/* Ten_pmax = floor(P*log(2)/log(5)) */
/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */

#if defined(IEEE_8087) + defined(IEEE_MC68k)
#define Exp_shift  20
#define Exp_shift1 20
#define Exp_msk1    0x100000
#define Exp_msk11   0x100000
#define Exp_mask  0x7ff00000
#define P 53
#define Bias 1023
#define IEEE_Arith
#define Emin (-1022)
#define Exp_1  0x3ff00000
#define Exp_11 0x3ff00000
#define Ebits 11
#define Frac_mask  0xfffff
#define Frac_mask1 0xfffff
#define Ten_pmax 22
#define Bletch 0x10
#define Bndry_mask  0xfffff
#define Bndry_mask1 0xfffff
#define LSB 1
#define Sign_bit 0x80000000
#define Log2P 1
#define Tiny0 0
#define Tiny1 1
#define Quick_max 14
#define Int_max 14
#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */
#else
#undef  Sudden_Underflow
#define Sudden_Underflow
#ifdef IBM
#define Exp_shift  24
#define Exp_shift1 24
#define Exp_msk1   0x1000000
#define Exp_msk11  0x1000000
#define Exp_mask  0x7f000000
#define P 14
#define Bias 65
#define Exp_1  0x41000000
#define Exp_11 0x41000000
#define Ebits 8	/* exponent has 7 bits, but 8 is the right value in b2d */
#define Frac_mask  0xffffff
#define Frac_mask1 0xffffff
#define Bletch 4
#define Ten_pmax 22
#define Bndry_mask  0xefffff
#define Bndry_mask1 0xffffff
#define LSB 1
#define Sign_bit 0x80000000
#define Log2P 4
#define Tiny0 0x100000
#define Tiny1 0
#define Quick_max 14
#define Int_max 15
#else /* VAX */
#define Exp_shift  23
#define Exp_shift1 7
#define Exp_msk1    0x80
#define Exp_msk11   0x800000
#define Exp_mask  0x7f80
#define P 56
#define Bias 129
#define Exp_1  0x40800000
#define Exp_11 0x4080
#define Ebits 8
#define Frac_mask  0x7fffff
#define Frac_mask1 0xffff007f
#define Ten_pmax 24
#define Bletch 2
#define Bndry_mask  0xffff007f
#define Bndry_mask1 0xffff007f
#define LSB 0x10000
#define Sign_bit 0x8000
#define Log2P 1
#define Tiny0 0x80
#define Tiny1 0
#define Quick_max 15
#define Int_max 15
#endif
#endif

#ifndef IEEE_Arith
#define ROUND_BIASED
#endif

#ifdef RND_PRODQUOT
#define rounded_product(a,b) a = rnd_prod(a, b)
#define rounded_quotient(a,b) a = rnd_quot(a, b)
extern double rnd_prod(double, double), rnd_quot(double, double);
#else
#define rounded_product(a,b) a *= b
#define rounded_quotient(a,b) a /= b
#endif

#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
#define Big1 0xffffffff

#ifndef Just_16
/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
 * This makes some inner loops simpler and sometimes saves work
 * during multiplications, but it often seems to make things slightly
 * slower.  Hence the default is now to store 32 bits per Long.
 */
#ifndef Pack_32
#define Pack_32
#endif
#endif

#define Kmax 15

struct Bigint {
	struct Bigint *next;
	int32 k, maxwds, sign, wds;
	ULong x[1];
};

typedef struct Bigint Bigint;

static Bigint *freelist[Kmax+1];

#ifdef JS_THREADSAFE
static PRLock *freelist_lock;
#endif

static Bigint *Balloc(int32 k)
{
	int32 x;
	Bigint *rv;

#ifdef JS_THREADSAFE
	PR_Lock(freelist_lock);
#endif
	if ((rv = freelist[k]) != NULL) {
		freelist[k] = rv->next;
	}
#ifdef JS_THREADSAFE
	PR_Unlock(freelist_lock);
#endif
	if (rv == NULL) {
		x = 1 << k;
		rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long));
		rv->k = k;
		rv->maxwds = x;
	}
	rv->sign = rv->wds = 0;
	return rv;
}

static void Bfree (Bigint *v)
{
	if (v) {
#ifdef JS_THREADSAFE
		PR_Lock(freelist_lock);
#endif
		v->next = freelist[v->k];
		freelist[v->k] = v;
#ifdef JS_THREADSAFE
		PR_Unlock(freelist_lock);
#endif
	}
}

#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
						  y->wds*sizeof(Long) + 2*sizeof(int32))

static Bigint *multadd(Bigint *b, int32 m, int32 a)	/* multiply by m and add a */
{
	int32 i, wds;
	ULong *x, y;
#ifdef Pack_32
	ULong xi, z;
#endif
	Bigint *b1;

	wds = b->wds;
	x = b->x;
	i = 0;
	do {
#ifdef Pack_32
		xi = *x;
		y = (xi & 0xffff) * m + a;
		z = (xi >> 16) * m + (y >> 16);
		a = (int32)(z >> 16);
		*x++ = (z << 16) + (y & 0xffff);
#else
		y = *x * m + a;
		a = (int32)(y >> 16);
		*x++ = y & 0xffff;
#endif
	}
	while(++i < wds);
	if (a) {
		if (wds >= b->maxwds) {
			b1 = Balloc(b->k+1);
			Bcopy(b1, b);
			Bfree(b);
			b = b1;
		}
		b->x[wds++] = a;
		b->wds = wds;
	}
	return b;
}

static Bigint *s2b(CONST char *s, int32 nd0, int32 nd, ULong y9)
{
	Bigint *b;
	int32 i, k;
	Long x, y;

	x = (nd + 8) / 9;
	for(k = 0, y = 1; x > y; y <<= 1, k++) ;
#ifdef Pack_32
	b = Balloc(k);
	b->x[0] = y9;
	b->wds = 1;
#else
	b = Balloc(k+1);
	b->x[0] = y9 & 0xffff;
	b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
#endif

	i = 9;
	if (9 < nd0) {
		s += 9;
		do b = multadd(b, 10, *s++ - '0');
		while(++i < nd0);
		s++;
	}
	else
		s += 10;
	for(; i < nd; i++)
		b = multadd(b, 10, *s++ - '0');
	return b;
}

static int32 hi0bits(register ULong x)
{
	register int32 k = 0;

	if (!(x & 0xffff0000)) {
		k = 16;
		x <<= 16;
	}
	if (!(x & 0xff000000)) {
		k += 8;
		x <<= 8;
	}
	if (!(x & 0xf0000000)) {
		k += 4;
		x <<= 4;
	}
	if (!(x & 0xc0000000)) {
		k += 2;
		x <<= 2;
	}
	if (!(x & 0x80000000)) {
		k++;
		if (!(x & 0x40000000))
			return 32;
	}
	return k;
}

static int32 lo0bits(ULong *y)
{
	register int32 k;
	register ULong x = *y;

	if (x & 7) {
		if (x & 1)
			return 0;
		if (x & 2) {
			*y = x >> 1;
			return 1;
		}
		*y = x >> 2;
		return 2;
	}
	k = 0;
	if (!(x & 0xffff)) {
		k = 16;
		x >>= 16;
	}
	if (!(x & 0xff)) {
		k += 8;
		x >>= 8;
	}
	if (!(x & 0xf)) {
		k += 4;
		x >>= 4;
	}
	if (!(x & 0x3)) {
		k += 2;
		x >>= 2;
	}
	if (!(x & 1)) {
		k++;
		x >>= 1;
		if (!x & 1)
			return 32;
	}
	*y = x;
	return k;
}

static Bigint *i2b(int32 i)
{
	Bigint *b;

	b = Balloc(1);
	b->x[0] = i;
	b->wds = 1;
	return b;
}

static Bigint *mult(CONST Bigint *a, CONST Bigint *b)
{
	CONST Bigint *t;
	Bigint *c;
	int32 k, wa, wb, wc;
	ULong carry, y, z;
        ULong *xc, *xc0, *xce;
	CONST ULong *x, *xa, *xae, *xb, *xbe;
#ifdef Pack_32
	ULong z2;
#endif

	if (a->wds < b->wds) {
		t = a;
		a = b;
		b = t;
	}
	k = a->k;
	wa = a->wds;
	wb = b->wds;
	wc = wa + wb;
	if (wc > a->maxwds)
		k++;
	c = Balloc(k);
	for(xc = c->x, xce = xc + wc; xc < xce; xc++)
		*xc = 0;
	xa = a->x;
	xae = xa + wa;
	xb = b->x;
	xbe = xb + wb;
	xc0 = c->x;
#ifdef Pack_32
	for(; xb < xbe; xb++, xc0++) {
		if ((y = *xb & 0xffff) != 0) {
			x = xa;
			xc = xc0;
			carry = 0;
			do {
				z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
				carry = z >> 16;
				z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
				carry = z2 >> 16;
				Storeinc(xc, z2, z);
			}
			while(x < xae);
			*xc = carry;
		}
		if ((y = *xb >> 16) != 0) {
			x = xa;
			xc = xc0;
			carry = 0;
			z2 = *xc;
			do {
				z = (*x & 0xffff) * y + (*xc >> 16) + carry;
				carry = z >> 16;
				Storeinc(xc, z, z2);
				z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
				carry = z2 >> 16;
			}
			while(x < xae);
			*xc = z2;
		}
	}
#else
	for(; xb < xbe; xc0++) {
		if (y = *xb++) {
			x = xa;
			xc = xc0;
			carry = 0;
			do {
				z = *x++ * y + *xc + carry;
				carry = z >> 16;
				*xc++ = z & 0xffff;
			}
			while(x < xae);
			*xc = carry;
		}
	}
#endif
	for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
	c->wds = wc;
	return c;
}

/*
 * 'p5s' points to a linked list of Bigints that are powers of 5.
 * This list grows on demand, and it can only grow: it won't change
 * in any other way.  So if we read 'p5s' or the 'next' field of
 * some Bigint on the list, and it is not NULL, we know it won't
 * change to NULL or some other value.  Only when the value of
 * 'p5s' or 'next' is NULL do we need to acquire the lock and add
 * a new Bigint to the list.
 */

static Bigint *p5s;

#ifdef JS_THREADSAFE
static PRLock *p5s_lock;
#endif

static Bigint *pow5mult(Bigint *b, int32 k)
{
	Bigint *b1, *p5, *p51;
	int32 i;
	static CONST int32 p05[3] = { 5, 25, 125 };

	if ((i = k & 3) != 0)
		b = multadd(b, p05[i-1], 0);

	if (!(k >>= 2))
		return b;
	if (!(p5 = p5s)) {
#ifdef JS_THREADSAFE
		/*
		 * We take great care to not call i2b() and Bfree()
		 * while holding the lock.
		 */
		Bigint *wasted_effort = NULL;
		p5 = i2b(625);
		/* lock and check again */
		PR_Lock(p5s_lock);
		if (!p5s) {
			/* first time */
			p5s = p5;
			p5->next = 0;
		} else {
			/* some other thread just beat us */
			wasted_effort = p5;
			p5 = p5s;
		}
		PR_Unlock(p5s_lock);
		if (wasted_effort) {
			Bfree(wasted_effort);
		}
#else
		/* first time */
		p5 = p5s = i2b(625);
		p5->next = 0;
#endif
	}
	for(;;) {
		if (k & 1) {
			b1 = mult(b, p5);
			Bfree(b);
			b = b1;
		}
		if (!(k >>= 1))
			break;
		if (!(p51 = p5->next)) {
#ifdef JS_THREADSAFE
			Bigint *wasted_effort = NULL;
			p51 = mult(p5, p5);
			PR_Lock(p5s_lock);
			if (!p5->next) {
				p5->next = p51;
				p51->next = 0;
			} else {
				wasted_effort = p51;
				p51 = p5->next;
			}
			PR_Unlock(p5s_lock);
			if (wasted_effort) {
				Bfree(wasted_effort);
			}
#else
			p51 = p5->next = mult(p5,p5);
			p51->next = 0;
#endif
		}
		p5 = p51;
	}
	return b;
}

static Bigint *lshift(Bigint *b, int32 k)
{
	int32 i, k1, n, n1;
	Bigint *b1;
	ULong *x, *x1, *xe, z;

#ifdef Pack_32
	n = k >> 5;
#else
	n = k >> 4;
#endif
	k1 = b->k;
	n1 = n + b->wds + 1;
	for(i = b->maxwds; n1 > i; i <<= 1)
		k1++;
	b1 = Balloc(k1);
	x1 = b1->x;
	for(i = 0; i < n; i++)
		*x1++ = 0;
	x = b->x;
	xe = x + b->wds;
#ifdef Pack_32
	if (k &= 0x1f) {
		k1 = 32 - k;
		z = 0;
		do {
			*x1++ = *x << k | z;
			z = *x++ >> k1;
		}
		while(x < xe);
		if ((*x1 = z) != 0)
			++n1;
	}
#else
	if (k &= 0xf) {
		k1 = 16 - k;
		z = 0;
		do {
			*x1++ = *x << k  & 0xffff | z;
			z = *x++ >> k1;
		}
		while(x < xe);
		if ((*x1 = z) != 0)
			++n1;
	}
#endif
	else do
		*x1++ = *x++;
		 while(x < xe);
	b1->wds = n1 - 1;
	Bfree(b);
	return b1;
}

static int32 cmp(Bigint *a, Bigint *b)
{
	ULong *xa, *xa0, *xb, *xb0;
	int32 i, j;

	i = a->wds;
	j = b->wds;
#ifdef DEBUG
	if ((i > 1 && !a->x[i-1]))
		Bug("cmp called with a->x[a->wds-1] == 0");
	if ((j > 1 && !b->x[j-1]))
		Bug("cmp called with b->x[b->wds-1] == 0");
#endif
	if (i -= j)
		return i;
	xa0 = a->x;
	xa = xa0 + j;
	xb0 = b->x;
	xb = xb0 + j;
	for(;;) {
		if (*--xa != *--xb)
			return *xa < *xb ? -1 : 1;
		if (xa <= xa0)
			break;
	}
	return 0;
}

static Bigint *diff(Bigint *a, Bigint *b)
{
	Bigint *c;
	int32 i, wa, wb;
	Long borrow, y;	/* We need signed shifts here. */
	ULong *xa, *xae, *xb, *xbe, *xc;
#ifdef Pack_32
	Long z;
#endif

	i = cmp(a,b);
	if (!i) {
		c = Balloc(0);
		c->wds = 1;
		c->x[0] = 0;
		return c;
	}
	if (i < 0) {
		c = a;
		a = b;
		b = c;
		i = 1;
	}
	else
		i = 0;
	c = Balloc(a->k);
	c->sign = i;
	wa = a->wds;
	xa = a->x;
	xae = xa + wa;
	wb = b->wds;
	xb = b->x;
	xbe = xb + wb;
	xc = c->x;
	borrow = 0;
#ifdef Pack_32
	do {
		y = (*xa & 0xffff) - (*xb & 0xffff) + borrow;
		borrow = y >> 16;
		Sign_Extend(borrow, y);
		z = (*xa++ >> 16) - (*xb++ >> 16) + borrow;
		borrow = z >> 16;
		Sign_Extend(borrow, z);
		Storeinc(xc, z, y);
	}
	while(xb < xbe);
	while(xa < xae) {
		y = (*xa & 0xffff) + borrow;
		borrow = y >> 16;
		Sign_Extend(borrow, y);
		z = (*xa++ >> 16) + borrow;
		borrow = z >> 16;
		Sign_Extend(borrow, z);
		Storeinc(xc, z, y);
	}
#else
	do {
		y = *xa++ - *xb++ + borrow;
		borrow = y >> 16;
		Sign_Extend(borrow, y);
		*xc++ = y & 0xffff;
	}
	while(xb < xbe);
	while(xa < xae) {
		y = *xa++ + borrow;
		borrow = y >> 16;
		Sign_Extend(borrow, y);
		*xc++ = y & 0xffff;
	}
#endif
	while(!*--xc)
		wa--;
	c->wds = wa;
	return c;
}

static double ulp(double x)
{
	register Long L;
	double a;

	L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
#ifndef Sudden_Underflow
	if (L > 0) {
#endif
#ifdef IBM
		L |= Exp_msk1 >> 4;
#endif
		word0(a) = L;
		word1(a) = 0;
#ifndef Sudden_Underflow
	}
	else {
		L = -L >> Exp_shift;
		if (L < Exp_shift) {
			word0(a) = 0x80000 >> L;
			word1(a) = 0;
		}
		else {
			word0(a) = 0;
			L -= Exp_shift;
			word1(a) = L >= 31 ? 1 : 1 << (31 - L);
		}
	}
#endif
	return a;
}

static double
b2d
#ifdef KR_headers
(a, e) Bigint *a; int32 *e;
#else
(Bigint *a, int32 *e)
#endif
{
	ULong *xa, *xa0, w, y, z;
	int32 k;
	double d;
#ifdef VAX
	ULong d0, d1;
#else
#define d0 word0(d)
#define d1 word1(d)
#endif

	xa0 = a->x;
	xa = xa0 + a->wds;
	y = *--xa;
#ifdef DEBUG
	if (!y) Bug("zero y in b2d");
#endif
	k = hi0bits(y);
	*e = 32 - k;
#ifdef Pack_32
	if (k < Ebits) {
		d0 = Exp_1 | y >> (Ebits - k);
		w = xa > xa0 ? *--xa : 0;
		d1 = y << (32-Ebits + k) | w >> (Ebits - k);
		goto ret_d;
	}
	z = xa > xa0 ? *--xa : 0;
	if (k -= Ebits) {
		d0 = Exp_1 | y << k | z >> (32 - k);
		y = xa > xa0 ? *--xa : 0;
		d1 = z << k | y >> (32 - k);
	}
	else {
		d0 = Exp_1 | y;
		d1 = z;
	}
#else
	if (k < Ebits + 16) {
		z = xa > xa0 ? *--xa : 0;
		d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
		w = xa > xa0 ? *--xa : 0;
		y = xa > xa0 ? *--xa : 0;
		d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
		goto ret_d;
	}
	z = xa > xa0 ? *--xa : 0;
	w = xa > xa0 ? *--xa : 0;
	k -= Ebits + 16;
	d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
	y = xa > xa0 ? *--xa : 0;
	d1 = w << k + 16 | y << k;
#endif
ret_d:
#ifdef VAX
	word0(d) = d0 >> 16 | d0 << 16;
	word1(d) = d1 >> 16 | d1 << 16;
#else
#undef d0
#undef d1
#endif
	return d;
}

static Bigint *
d2b
#ifdef KR_headers
(d, e, bits) double d; int32 *e, *bits;
#else
(double d, int32 *e, int32 *bits)
#endif
{
	Bigint *b;
	int32 de, i, k;
	ULong *x, y, z;
#ifdef VAX
	ULong d0, d1;
	d0 = word0(d) >> 16 | word0(d) << 16;
	d1 = word1(d) >> 16 | word1(d) << 16;
#else
#define d0 word0(d)
#define d1 word1(d)
#endif

#ifdef Pack_32
	b = Balloc(1);
#else
	b = Balloc(2);
#endif
	x = b->x;

	z = d0 & Frac_mask;
	d0 &= 0x7fffffff;	/* clear sign bit, which we ignore */
#ifdef Sudden_Underflow
	de = (int32)(d0 >> Exp_shift);
#ifndef IBM
	z |= Exp_msk11;
#endif
#else
	if ((de = (int32)(d0 >> Exp_shift)) != 0)
		z |= Exp_msk1;
#endif
#ifdef Pack_32
	if ((y = d1) != 0) {
		if ((k = lo0bits(&y)) != 0) {
			x[0] = y | z << (32 - k);
			z >>= k;
		}
		else
			x[0] = y;
		i = b->wds = (x[1] = z) ? 2 : 1;
	}
	else {
#ifdef DEBUG
		if (!z)
			Bug("Zero passed to d2b");
#endif
		k = lo0bits(&z);
		x[0] = z;
		i = b->wds = 1;
		k += 32;
	}
#else
	if ((y = d1) != 0) {
		if ((k = lo0bits(&y)) != 0)
			if (k >= 16) {
				x[0] = y | z << 32 - k & 0xffff;
				x[1] = z >> k - 16 & 0xffff;
				x[2] = z >> k;
				i = 2;
			}
			else {
				x[0] = y & 0xffff;
				x[1] = y >> 16 | z << 16 - k & 0xffff;
				x[2] = z >> k & 0xffff;
				x[3] = z >> k+16;
				i = 3;
			}
		else {
			x[0] = y & 0xffff;
			x[1] = y >> 16;
			x[2] = z & 0xffff;
			x[3] = z >> 16;
			i = 3;
		}
	}
	else {
#ifdef DEBUG
		if (!z)
			Bug("Zero passed to d2b");
#endif
		k = lo0bits(&z);
		if (k >= 16) {
			x[0] = z;
			i = 0;
		}
		else {
			x[0] = z & 0xffff;
			x[1] = z >> 16;
			i = 1;
		}
		k += 32;
	}
	while(!x[i])
		--i;
	b->wds = i + 1;
#endif
#ifndef Sudden_Underflow
	if (de) {
#endif
#ifdef IBM
		*e = (de - Bias - (P-1) << 2) + k;
		*bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
#else
		*e = de - Bias - (P-1) + k;
		*bits = P - k;
#endif
#ifndef Sudden_Underflow
	}
	else {
		*e = de - Bias - (P-1) + 1 + k;
#ifdef Pack_32
		*bits = 32*i - hi0bits(x[i-1]);
#else
		*bits = (i+2)*16 - hi0bits(x[i]);
#endif
	}
#endif
	return b;
}
#undef d0
#undef d1

static double
ratio
#ifdef KR_headers
(a, b) Bigint *a, *b;
#else
(Bigint *a, Bigint *b)
#endif
{
	double da, db;
	int32 k, ka, kb;

	da = b2d(a, &ka);
	db = b2d(b, &kb);
#ifdef Pack_32
	k = ka - kb + 32*(a->wds - b->wds);
#else
	k = ka - kb + 16*(a->wds - b->wds);
#endif
#ifdef IBM
	if (k > 0) {
		word0(da) += (k >> 2)*Exp_msk1;
		if (k &= 3)
			da *= 1 << k;
	}
	else {
		k = -k;
		word0(db) += (k >> 2)*Exp_msk1;
		if (k &= 3)
			db *= 1 << k;
	}
#else
	if (k > 0)
		word0(da) += k*Exp_msk1;
	else {
		k = -k;
		word0(db) += k*Exp_msk1;
	}
#endif
	return da / db;
}

static CONST double
tens[] = {
	1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
	1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
	1e20, 1e21, 1e22
#ifdef VAX
	, 1e23, 1e24
#endif
};

static CONST double
#ifdef IEEE_Arith
bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
#define n_bigtens 5
#else
#ifdef IBM
bigtens[] = { 1e16, 1e32, 1e64 };
static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };
#define n_bigtens 3
#else
bigtens[] = { 1e16, 1e32 };
static CONST double tinytens[] = { 1e-16, 1e-32 };
#define n_bigtens 2
#endif
#endif

#ifdef JS_THREADSAFE
static JSBool initialized = JS_FALSE;

/* hacked replica of nspr _PR_InitDtoa */
static void InitDtoa(void)
{
	freelist_lock = PR_NewLock();
        p5s_lock = PR_NewLock();
	initialized = JS_TRUE;
}
#endif


/* nspr2 watcom bug ifdef omitted */

JS_FRIEND_API(double)
JS_strtod(CONST char *s00, char **se)
{
	int32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign,
		e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
	CONST char *s, *s0, *s1;
	double aadj, aadj1, adj, rv, rv0;
	Long L;
	ULong y, z;
	Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;

#ifdef JS_THREADSAFE
	if (!initialized) InitDtoa();
#endif

        bb = bd = bs = delta = NULL;
	sign = nz0 = nz = 0;
	rv = 0.;
	for(s = s00;;s++) switch(*s) {
	case '-':
		sign = 1;
		/* no break */
	case '+':
		if (*++s)
			goto break2;
		/* no break */
	case 0:
		s = s00;
		goto ret;
	case '\t':
	case '\n':
	case '\v':
	case '\f':
	case '\r':
	case ' ':
		continue;
	default:
		goto break2;
	}
break2:
	if (*s == '0') {
		nz0 = 1;
		while(*++s == '0') ;
		if (!*s)
			goto ret;
	}
	s0 = s;
	y = z = 0;
	for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
		if (nd < 9)
			y = 10*y + c - '0';
		else if (nd < 16)
			z = 10*z + c - '0';
	nd0 = nd;
	if (c == '.') {
		c = *++s;
		if (!nd) {
			for(; c == '0'; c = *++s)
				nz++;
			if (c > '0' && c <= '9') {
				s0 = s;
				nf += nz;
				nz = 0;
				goto have_dig;
			}
			goto dig_done;
		}
		for(; c >= '0' && c <= '9'; c = *++s) {
		have_dig:
			nz++;
			if (c -= '0') {
				nf += nz;
				for(i = 1; i < nz; i++)
					if (nd++ < 9)
						y *= 10;
					else if (nd <= DBL_DIG + 1)
						z *= 10;
				if (nd++ < 9)
					y = 10*y + c;
				else if (nd <= DBL_DIG + 1)
					z = 10*z + c;
				nz = 0;
			}
		}
	}
dig_done:
	e = 0;
	if (c == 'e' || c == 'E') {
		if (!nd && !nz && !nz0) {
			s = s00;
			goto ret;
		}
		s00 = s;
		esign = 0;
		switch(c = *++s) {
		case '-':
			esign = 1;
		case '+':
			c = *++s;
		}
		if (c >= '0' && c <= '9') {
			while(c == '0')
				c = *++s;
			if (c > '0' && c <= '9') {
				L = c - '0';
				s1 = s;
				while((c = *++s) >= '0' && c <= '9')
					L = 10*L + c - '0';
				if (s - s1 > 8 || L > 19999)
					/* Avoid confusion from exponents
					 * so large that e might overflow.
					 */
					e = 19999; /* safe for 16 bit ints */
				else
					e = (int32)L;
				if (esign)
					e = -e;
			}
			else
				e = 0;
		}
		else
			s = s00;
	}
	if (!nd) {
		if (!nz && !nz0)
			s = s00;
		goto ret;
	}
	e1 = e -= nf;

	/* Now we have nd0 digits, starting at s0, followed by a
	 * decimal point, followed by nd-nd0 digits.  The number we're
	 * after is the integer represented by those digits times
	 * 10**e */

	if (!nd0)
		nd0 = nd;
	k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
	rv = y;
	if (k > 9)
		rv = tens[k - 9] * rv + z;
	bd0 = 0;
	if (nd <= DBL_DIG
#ifndef RND_PRODQUOT
		&& FLT_ROUNDS == 1
#endif
		) {
		if (!e)
			goto ret;
		if (e > 0) {
			if (e <= Ten_pmax) {
#ifdef VAX
				goto vax_ovfl_check;
#else
				/* rv = */ rounded_product(rv, tens[e]);
				goto ret;
#endif
			}
			i = DBL_DIG - nd;
			if (e <= Ten_pmax + i) {
				/* A fancier test would sometimes let us do
				 * this for larger i values.
				 */
				e -= i;
				rv *= tens[i];
#ifdef VAX
				/* VAX exponent range is so narrow we must
				 * worry about overflow here...
				 */
			vax_ovfl_check:
				word0(rv) -= P*Exp_msk1;
				/* rv = */ rounded_product(rv, tens[e]);
				if ((word0(rv) & Exp_mask)
					> Exp_msk1*(DBL_MAX_EXP+Bias-1-P))
					goto ovfl;
				word0(rv) += P*Exp_msk1;
#else
				/* rv = */ rounded_product(rv, tens[e]);
#endif
				goto ret;
			}
		}
#ifndef Inaccurate_Divide
		else if (e >= -Ten_pmax) {
			/* rv = */ rounded_quotient(rv, tens[-e]);
			goto ret;
		}
#endif
	}
	e1 += nd - k;

	/* Get starting approximation = rv * 10**e1 */

	if (e1 > 0) {
		if ((i = e1 & 15) != 0)
			rv *= tens[i];
		if (e1 &= ~15) {
			if (e1 > DBL_MAX_10_EXP) {
			ovfl:
				errno = ERANGE;
#ifdef __STDC__
				rv = HUGE_VAL;
#else
				/* Can't trust HUGE_VAL */
#ifdef IEEE_Arith
				word0(rv) = Exp_mask;
				word1(rv) = 0;
#else
				word0(rv) = Big0;
				word1(rv) = Big1;
#endif
#endif
				if (bd0)
					goto retfree;
				goto ret;
			}
			if (e1 >>= 4) {
				for(j = 0; e1 > 1; j++, e1 >>= 1)
					if (e1 & 1)
						rv *= bigtens[j];
				/* The last multiplication could overflow. */
				word0(rv) -= P*Exp_msk1;
				rv *= bigtens[j];
				if ((z = word0(rv) & Exp_mask)
					> Exp_msk1*(DBL_MAX_EXP+Bias-P))
					goto ovfl;
				if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
					/* set to largest number */
					/* (Can't trust DBL_MAX) */
					word0(rv) = Big0;
					word1(rv) = Big1;
				}
				else
					word0(rv) += P*Exp_msk1;
			}

		}
	}
	else if (e1 < 0) {
		e1 = -e1;
		if ((i = e1 & 15) != 0)
			rv /= tens[i];
		if (e1 &= ~15) {
			e1 >>= 4;
			if (e1 >= 1 << n_bigtens)
				goto undfl;
			for(j = 0; e1 > 1; j++, e1 >>= 1)
				if (e1 & 1)
					rv *= tinytens[j];
			/* The last multiplication could underflow. */
			rv0 = rv;
			rv *= tinytens[j];
			if (!rv) {
				rv = 2.*rv0;
				rv *= tinytens[j];
				if (!rv) {
				undfl:
					rv = 0.;
					errno = ERANGE;
					if (bd0)
						goto retfree;
					goto ret;
				}
				word0(rv) = Tiny0;
				word1(rv) = Tiny1;
				/* The refinement below will clean
				 * this approximation up.
				 */
			}
		}
	}

	/* Now the hard part -- adjusting rv to the correct value.*/

	/* Put digits into bd: true value = bd * 10^e */

	bd0 = s2b(s0, nd0, nd, y);

	for(;;) {
		bd = Balloc(bd0->k);
		Bcopy(bd, bd0);
		bb = d2b(rv, &bbe, &bbbits);	/* rv = bb * 2^bbe */
		bs = i2b(1);

		if (e >= 0) {
			bb2 = bb5 = 0;
			bd2 = bd5 = e;
		}
		else {
			bb2 = bb5 = -e;
			bd2 = bd5 = 0;
		}
		if (bbe >= 0)
			bb2 += bbe;
		else
			bd2 -= bbe;
		bs2 = bb2;
#ifdef Sudden_Underflow
#ifdef IBM
		j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
#else
		j = P + 1 - bbbits;
#endif
#else
		i = bbe + bbbits - 1;	/* logb(rv) */
		if (i < Emin)	/* denormal */
			j = bbe + (P-Emin);
		else
			j = P + 1 - bbbits;
#endif
		bb2 += j;
		bd2 += j;
		i = bb2 < bd2 ? bb2 : bd2;
		if (i > bs2)
			i = bs2;
		if (i > 0) {
			bb2 -= i;
			bd2 -= i;
			bs2 -= i;
		}
		if (bb5 > 0) {
			bs = pow5mult(bs, bb5);
			bb1 = mult(bs, bb);
			Bfree(bb);
			bb = bb1;
		}
		if (bb2 > 0)
			bb = lshift(bb, bb2);
		if (bd5 > 0)
			bd = pow5mult(bd, bd5);
		if (bd2 > 0)
			bd = lshift(bd, bd2);
		if (bs2 > 0)
			bs = lshift(bs, bs2);
		delta = diff(bb, bd);
		dsign = delta->sign;
		delta->sign = 0;
		i = cmp(delta, bs);
		if (i < 0) {
			/* Error is less than half an ulp -- check for
			 * special case of mantissa a power of two.
			 */
			if (dsign || word1(rv) || word0(rv) & Bndry_mask)
				break;
			delta = lshift(delta,Log2P);
			if (cmp(delta, bs) > 0)
				goto drop_down;
			break;
		}
		if (i == 0) {
			/* exactly half-way between */
			if (dsign) {
				if ((word0(rv) & Bndry_mask1) == Bndry_mask1
					&&  word1(rv) == 0xffffffff) {
					/*boundary case -- increment exponent*/
					word0(rv) = (word0(rv) & Exp_mask)
						+ Exp_msk1
#ifdef IBM
						| Exp_msk1 >> 4
#endif
						;
					word1(rv) = 0;
					break;
				}
			}
			else if (!(word0(rv) & Bndry_mask) && !word1(rv)) {
			drop_down:
				/* boundary case -- decrement exponent */
#ifdef Sudden_Underflow
				L = word0(rv) & Exp_mask;
#ifdef IBM
				if (L <  Exp_msk1)
#else
					if (L <= Exp_msk1)
#endif
						goto undfl;
				L -= Exp_msk1;
#else
				L = (word0(rv) & Exp_mask) - Exp_msk1;
#endif
				word0(rv) = L | Bndry_mask1;
				word1(rv) = 0xffffffff;
#ifdef IBM
				goto cont;
#else
				break;
#endif
			}
#ifndef ROUND_BIASED
			if (!(word1(rv) & LSB))
				break;
#endif
			if (dsign)
				rv += ulp(rv);
#ifndef ROUND_BIASED
			else {
				rv -= ulp(rv);
#ifndef Sudden_Underflow
				if (!rv)
					goto undfl;
#endif
			}
#endif
			break;
		}
		if ((aadj = ratio(delta, bs)) <= 2.) {
			if (dsign)
				aadj = aadj1 = 1.;
			else if (word1(rv) || word0(rv) & Bndry_mask) {
#ifndef Sudden_Underflow
				if (word1(rv) == Tiny1 && !word0(rv))
					goto undfl;
#endif
				aadj = 1.;
				aadj1 = -1.;
			}
			else {
				/* special case -- power of FLT_RADIX to be */
				/* rounded down... */

				if (aadj < 2./FLT_RADIX)
					aadj = 1./FLT_RADIX;
				else
					aadj *= 0.5;
				aadj1 = -aadj;
			}
		}
		else {
			aadj *= 0.5;
			aadj1 = dsign ? aadj : -aadj;
#ifdef Check_FLT_ROUNDS
			switch(FLT_ROUNDS) {
			case 2: /* towards +infinity */
				aadj1 -= 0.5;
				break;
			case 0: /* towards 0 */
			case 3: /* towards -infinity */
				aadj1 += 0.5;
			}
#else
			if (FLT_ROUNDS == 0)
				aadj1 += 0.5;
#endif
		}
		y = word0(rv) & Exp_mask;

		/* Check for overflow */

		if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
			rv0 = rv;
			word0(rv) -= P*Exp_msk1;
			adj = aadj1 * ulp(rv);
			rv += adj;
			if ((word0(rv) & Exp_mask) >=
				Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
				if (word0(rv0) == Big0 && word1(rv0) == Big1)
					goto ovfl;
				word0(rv) = Big0;
				word1(rv) = Big1;
				goto cont;
			}
			else
				word0(rv) += P*Exp_msk1;
		}
		else {
#ifdef Sudden_Underflow
			if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {
				rv0 = rv;
				word0(rv) += P*Exp_msk1;
				adj = aadj1 * ulp(rv);
				rv += adj;
#ifdef IBM
				if ((word0(rv) & Exp_mask) <  P*Exp_msk1)
#else
					if ((word0(rv) & Exp_mask) <= P*Exp_msk1)
#endif
						{
							if (word0(rv0) == Tiny0
								&& word1(rv0) == Tiny1)
								goto undfl;
							word0(rv) = Tiny0;
							word1(rv) = Tiny1;
							goto cont;
						}
					else
						word0(rv) -= P*Exp_msk1;
			}
			else {
				adj = aadj1 * ulp(rv);
				rv += adj;
			}
#else
			/* Compute adj so that the IEEE rounding rules will
			 * correctly round rv + adj in some half-way cases.
			 * If rv * ulp(rv) is denormalized (i.e.,
			 * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
			 * trouble from bits lost to denormalization;
			 * example: 1.2e-307 .
			 */
			if (y <= (P-1)*Exp_msk1 && aadj >= 1.) {
				aadj1 = (double)(int32)(aadj + 0.5);
				if (!dsign)
					aadj1 = -aadj1;
			}
			adj = aadj1 * ulp(rv);
			rv += adj;
#endif
		}
		z = word0(rv) & Exp_mask;
		if (y == z) {
			/* Can we stop now? */
			L = (Long)aadj;
			aadj -= L;
			/* The tolerances below are conservative. */
			if (dsign || word1(rv) || word0(rv) & Bndry_mask) {
				if (aadj < .4999999 || aadj > .5000001)
					break;
			}
			else if (aadj < .4999999/FLT_RADIX)
				break;
		}
	cont:
		Bfree(bb);
		Bfree(bd);
		Bfree(bs);
		Bfree(delta);
	}
retfree:
	Bfree(bb);
	Bfree(bd);
	Bfree(bs);
	Bfree(bd0);
	Bfree(delta);
ret:
	if (se)
		*se = (char *)s;
	return sign ? -rv : rv;
}

static int32
quorem(Bigint *b, Bigint *S)
{
	int32 n;
	Long borrow, y;
	ULong carry, q, ys;
	ULong *bx, *bxe, *sx, *sxe;
#ifdef Pack_32
	Long z;
	ULong si, zs;
#endif

	n = S->wds;
#ifdef DEBUG
	/*debug*/ if (b->wds > n)
		/*debug*/	Bug("oversize b in quorem");
#endif
	if (b->wds < n)
		return 0;
	sx = S->x;
	sxe = sx + --n;
	bx = b->x;
	bxe = bx + n;
	q = *bxe / (*sxe + 1);	/* ensure q <= true quotient */
#ifdef DEBUG
	/*debug*/ if (q > 9)
		/*debug*/	Bug("oversized quotient in quorem");
#endif
	if (q) {
		borrow = 0;
		carry = 0;
		do {
#ifdef Pack_32
			si = *sx++;
			ys = (si & 0xffff) * q + carry;
			zs = (si >> 16) * q + (ys >> 16);
			carry = zs >> 16;
			y = (*bx & 0xffff) - (ys & 0xffff) + borrow;
			borrow = y >> 16;
			Sign_Extend(borrow, y);
			z = (*bx >> 16) - (zs & 0xffff) + borrow;
			borrow = z >> 16;
			Sign_Extend(borrow, z);
			Storeinc(bx, z, y);
#else
			ys = *sx++ * q + carry;
			carry = ys >> 16;
			y = *bx - (ys & 0xffff) + borrow;
			borrow = y >> 16;
			Sign_Extend(borrow, y);
			*bx++ = y & 0xffff;
#endif
		}
		while(sx <= sxe);
		if (!*bxe) {
			bx = b->x;
			while(--bxe > bx && !*bxe)
				--n;
			b->wds = n;
		}
	}
	if (cmp(b, S) >= 0) {
		q++;
		borrow = 0;
		carry = 0;
		bx = b->x;
		sx = S->x;
		do {
#ifdef Pack_32
			si = *sx++;
			ys = (si & 0xffff) + carry;
			zs = (si >> 16) + (ys >> 16);
			carry = zs >> 16;
			y = (*bx & 0xffff) - (ys & 0xffff) + borrow;
			borrow = y >> 16;
			Sign_Extend(borrow, y);
			z = (*bx >> 16) - (zs & 0xffff) + borrow;
			borrow = z >> 16;
			Sign_Extend(borrow, z);
			Storeinc(bx, z, y);
#else
			ys = *sx++ + carry;
			carry = ys >> 16;
			y = *bx - (ys & 0xffff) + borrow;
			borrow = y >> 16;
			Sign_Extend(borrow, y);
			*bx++ = y & 0xffff;
#endif
		}
		while(sx <= sxe);
		bx = b->x;
		bxe = bx + n;
		if (!*bxe) {
			while(--bxe > bx && !*bxe)
				--n;
			b->wds = n;
		}
	}
	return (int)q;
}

/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
 *
 * Inspired by "How to Print Floating-Point Numbers Accurately" by
 * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
 *
 * Modifications:
 *	1. Rather than iterating, we use a simple numeric overestimate
 *	   to determine k = floor(log10(d)).  We scale relevant
 *	   quantities using O(log2(k)) rather than O(k) multiplications.
 *	2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
 *	   try to generate digits strictly left to right.  Instead, we
 *	   compute with fewer bits and propagate the carry if necessary
 *	   when rounding the final digit up.  This is often faster.
 *	3. Under the assumption that input will be rounded nearest,
 *	   mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
 *	   That is, we allow equality in stopping tests when the
 *	   round-nearest rule will give the same floating-point value
 *	   as would satisfaction of the stopping test with strict
 *	   inequality.
 *	4. We remove common factors of powers of 2 from relevant
 *	   quantities.
 *	5. When converting floating-point integers less than 1e16,
 *	   we use floating-point arithmetic rather than resorting
 *	   to multiple-precision integers.
 *	6. When asked to produce fewer than 15 digits, we first try
 *	   to get by with floating-point arithmetic; we resort to
 *	   multiple-precision integer arithmetic only if we cannot
 *	   guarantee that the floating-point calculation has given
 *	   the correctly rounded result.  For k requested digits and
 *	   "uniformly" distributed input, the probability is
 *	   something like 10^(k-15) that we must resort to the Long
 *	   calculation.
 */

static JSBool
JS_dtoa(double d, int mode, int ndigits,
	int *decpt, int *sign, char **rve, char *buf, size_t bufsize)
{
	/*	Arguments ndigits, decpt, sign are similar to those
		of ecvt and fcvt; trailing zeros are suppressed from
		the returned string.  If not null, *rve is set to point
		to the end of the return value.  If d is +-Infinity or NaN,
		then *decpt is set to 9999.

		mode:
		0 ==> shortest string that yields d when read in
		and rounded to nearest.
		1 ==> like 0, but with Steele & White stopping rule;
		e.g. with IEEE P754 arithmetic , mode 0 gives
		1e23 whereas mode 1 gives 9.999999999999999e22.
		2 ==> max(1,ndigits) significant digits.  This gives a
		return value similar to that of ecvt, except
		that trailing zeros are suppressed.
		3 ==> through ndigits past the decimal point.  This
		gives a return value similar to that from fcvt,
		except that trailing zeros are suppressed, and
		ndigits can be negative.
		4-9 should give the same return values as 2-3, i.e.,
		4 <= mode <= 9 ==> same return as mode
		2 + (mode & 1).  These modes are mainly for
		debugging; often they run slower but sometimes
		faster than modes 2-3.
		4,5,8,9 ==> left-to-right digit generation.
		6-9 ==> don't try fast floating-point estimate
		(if applicable).

		Values of mode other than 0-9 are treated as mode 0.

		Sufficient space is allocated to the return value
		to hold the suppressed trailing zeros.
	*/

	int32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
		j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
		spec_case, try_quick;
	Long L;
#ifndef Sudden_Underflow
	int32 denorm;
	ULong x;
#endif
	Bigint *b, *b1, *delta, *mlo, *mhi, *S;
	double d2, ds, eps;
	char *s, *s0;
	Bigint *result = 0;
	static int32 result_k;
	JSBool retval;
        size_t strsize;

        spec_case = 0;   /* Not a power-of-two special case */
        ilim = ilim1 = 0;
        mlo = NULL;

#ifdef JS_THREADSAFE
	if (!initialized) InitDtoa();
#endif

	if (word0(d) & Sign_bit) {
		/* set sign for everything, including 0's and NaNs */
		*sign = 1;
		word0(d) &= ~Sign_bit;	/* clear sign bit */
	}
	else
		*sign = 0;

#if defined(IEEE_Arith) + defined(VAX)
#ifdef IEEE_Arith
	if ((word0(d) & Exp_mask) == Exp_mask)
#else
		if (word0(d)  == 0x8000)
#endif
			{
				/* Infinity or NaN */
				*decpt = 9999;
				s =
#ifdef IEEE_Arith
					!word1(d) && !(word0(d) & 0xfffff) ? "Infinity" :
#endif
					"NaN";
				if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) {
					JS_ASSERT(JS_FALSE);
/* 					JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */
					return JS_FALSE;
				}
				strcpy(buf, s);
				if (rve) {
					*rve =
#ifdef IEEE_Arith
						buf[3] ? buf + 8 :
#endif
					buf + 3;
					JS_ASSERT(**rve == '\0');
                                }
				return JS_TRUE;
			}
#endif
#ifdef IBM
	d += 0; /* normalize */
#endif
	if (!d) {
		*decpt = 1;
		if (bufsize < 2) {
			JS_ASSERT(JS_FALSE);
/* 			JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */
			return JS_FALSE;
		}
		buf[0] = '0'; buf[1] = '\0';  /* copy "0" to buffer */
		if (rve) {
			*rve = buf + 1;
			JS_ASSERT(**rve == '\0');
		}
		return JS_TRUE;
	}

	b = d2b(d, &be, &bbits);
#ifdef Sudden_Underflow
	i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
#else
	if ((i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) {
#endif
		d2 = d;
		word0(d2) &= Frac_mask1;
		word0(d2) |= Exp_11;
#ifdef IBM
		if (j = 11 - hi0bits(word0(d2) & Frac_mask))
			d2 /= 1 << j;
#endif

		/* log(x)	~=~ log(1.5) + (x-1.5)/1.5
		 * log10(x)	 =  log(x) / log(10)
		 *		~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
		 * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
		 *
		 * This suggests computing an approximation k to log10(d) by
		 *
		 * k = (i - Bias)*0.301029995663981
		 *	+ ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
		 *
		 * We want k to be too large rather than too small.
		 * The error in the first-order Taylor series approximation
		 * is in our favor, so we just round up the constant enough
		 * to compensate for any error in the multiplication of
		 * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
		 * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
		 * adding 1e-13 to the constant term more than suffices.
		 * Hence we adjust the constant term to 0.1760912590558.
		 * (We could get a more accurate k by invoking log10,
		 *  but this is probably not worthwhile.)
		 */

		i -= Bias;
#ifdef IBM
		i <<= 2;
		i += j;
#endif
#ifndef Sudden_Underflow
		denorm = 0;
	}
	else {
		/* d is denormalized */

		i = bbits + be + (Bias + (P-1) - 1);
		x = i > 32  ? word0(d) << (64 - i) | word1(d) >> (i - 32)
			: word1(d) << (32 - i);
		d2 = x;
		word0(d2) -= 31*Exp_msk1; /* adjust exponent */
		i -= (Bias + (P-1) - 1) + 1;
		denorm = 1;
	}
#endif
	ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
	k = (int32)ds;
	if (ds < 0. && ds != k)
		k--;	/* want k = floor(ds) */
	k_check = 1;
	if (k >= 0 && k <= Ten_pmax) {
		if (d < tens[k])
			k--;
		k_check = 0;
	}
	j = bbits - i - 1;
	if (j >= 0) {
		b2 = 0;
		s2 = j;
	}
	else {
		b2 = -j;
		s2 = 0;
	}
	if (k >= 0) {
		b5 = 0;
		s5 = k;
		s2 += k;
	}
	else {
		b2 -= k;
		b5 = -k;
		s5 = 0;
	}
	if (mode < 0 || mode > 9)
		mode = 0;
	try_quick = 1;
	if (mode > 5) {
		mode -= 4;
		try_quick = 0;
	}
	leftright = 1;
	switch(mode) {
	case 0:
	case 1:
		ilim = ilim1 = -1;
		i = 18;
		ndigits = 0;
		break;
	case 2:
		leftright = 0;
		/* no break */
	case 4:
		if (ndigits <= 0)
			ndigits = 1;
		ilim = ilim1 = i = ndigits;
		break;
	case 3:
		leftright = 0;
		/* no break */
	case 5:
		i = ndigits + k + 1;
		ilim = i;
		ilim1 = i - 1;
		if (i <= 0)
			i = 1;
	}
	j = sizeof(ULong);
	for(result_k = 0; sizeof(Bigint) - sizeof(ULong) <= (unsigned)i - j;
		j <<= 1) result_k++;
	result = Balloc(result_k);
	s = s0 = (char *)result;

	if (ilim >= 0 && ilim <= Quick_max && try_quick) {

		/* Try to get by with floating-point arithmetic. */

		i = 0;
		d2 = d;
		k0 = k;
		ilim0 = ilim;
		ieps = 2; /* conservative */
		if (k > 0) {
			ds = tens[k&0xf];
			j = k >> 4;
			if (j & Bletch) {
				/* prevent overflows */
				j &= Bletch - 1;
				d /= bigtens[n_bigtens-1];
				ieps++;
			}
			for(; j; j >>= 1, i++)
				if (j & 1) {
					ieps++;
					ds *= bigtens[i];
				}
			d /= ds;
		}
		else if ((j1 = -k) != 0) {
			d *= tens[j1 & 0xf];
			for(j = j1 >> 4; j; j >>= 1, i++)
				if (j & 1) {
					ieps++;
					d *= bigtens[i];
				}
		}
		if (k_check && d < 1. && ilim > 0) {
			if (ilim1 <= 0)
				goto fast_failed;
			ilim = ilim1;
			k--;
			d *= 10.;
			ieps++;
		}
		eps = ieps*d + 7.;
		word0(eps) -= (P-1)*Exp_msk1;
		if (ilim == 0) {
			S = mhi = 0;
			d -= 5.;
			if (d > eps)
				goto one_digit;
			if (d < -eps)
				goto no_digits;
			goto fast_failed;
		}
#ifndef No_leftright
		if (leftright) {
			/* Use Steele & White method of only
			 * generating digits needed.
			 */
			eps = 0.5/tens[ilim-1] - eps;
			for(i = 0;;) {
				L = (Long)d;
				d -= L;
				*s++ = '0' + (char)L;
				if (d < eps)
					goto ret1;
				if (1. - d < eps)
					goto bump_up;
				if (++i >= ilim)
					break;
				eps *= 10.;
				d *= 10.;
			}
		}
		else {
#endif
			/* Generate ilim digits, then fix them up. */
			eps *= tens[ilim-1];
			for(i = 1;; i++, d *= 10.) {
				L = (Long)d;
				d -= L;
				*s++ = '0' + (char)L;
				if (i == ilim) {
					if (d > 0.5 + eps)
						goto bump_up;
					else if (d < 0.5 - eps) {
						while(*--s == '0') ;
						s++;
						goto ret1;
					}
					break;
				}
			}
#ifndef No_leftright
		}
#endif
	fast_failed:
		s = s0;
		d = d2;
		k = k0;
		ilim = ilim0;
	}

	/* Do we have a "small" integer? */

	if (be >= 0 && k <= Int_max) {
		/* Yes. */
		ds = tens[k];
		if (ndigits < 0 && ilim <= 0) {
			S = mhi = 0;
			if (ilim < 0 || d <= 5*ds)
				goto no_digits;
			goto one_digit;
		}
		for(i = 1;; i++) {
			L = (Long) (d / ds);
			d -= L*ds;
#ifdef Check_FLT_ROUNDS
			/* If FLT_ROUNDS == 2, L will usually be high by 1 */
			if (d < 0) {
				L--;
				d += ds;
			}
#endif
			*s++ = '0' + (char)L;
			if (i == ilim) {
				d += d;
				if ((d > ds) || (d == ds && L & 1)) {
				bump_up:
					while(*--s == '9')
						if (s == s0) {
							k++;
							*s = '0';
							break;
						}
					++*s++;
				}
				break;
			}
			if (!(d *= 10.))
				break;
		}
		goto ret1;
	}

	m2 = b2;
	m5 = b5;
	mhi = mlo = 0;
	if (leftright) {
		if (mode < 2) {
			i =
#ifndef Sudden_Underflow
				denorm ? be + (Bias + (P-1) - 1 + 1) :
#endif
#ifdef IBM
				1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
#else
			1 + P - bbits;
#endif
		}
		else {
			j = ilim - 1;
			if (m5 >= j)
				m5 -= j;
			else {
				s5 += j -= m5;
				b5 += j;
				m5 = 0;
			}
			if ((i = ilim) < 0) {
				m2 -= i;
				i = 0;
			}
		}
		b2 += i;
		s2 += i;
		mhi = i2b(1);
	}
	if (m2 > 0 && s2 > 0) {
		i = m2 < s2 ? m2 : s2;
		b2 -= i;
		m2 -= i;
		s2 -= i;
	}
	if (b5 > 0) {
		if (leftright) {
			if (m5 > 0) {
				mhi = pow5mult(mhi, m5);
				b1 = mult(mhi, b);
				Bfree(b);
				b = b1;
			}
			if ((j = b5 - m5) != 0)
				b = pow5mult(b, j);
		}
		else
			b = pow5mult(b, b5);
	}
	S = i2b(1);
	if (s5 > 0)
		S = pow5mult(S, s5);

	/* Check for special case that d is a normalized power of 2. */

	if (mode < 2) {
		if (!word1(d) && !(word0(d) & Bndry_mask)
#ifndef Sudden_Underflow
			&& word0(d) & Exp_mask
#endif
			) {
			/* The special case */
			b2 += Log2P;
			s2 += Log2P;
			spec_case = 1;
		}
		else
			spec_case = 0;
	}

	/* Arrange for convenient computation of quotients:
	 * shift left if necessary so divisor has 4 leading 0 bits.
	 *
	 * Perhaps we should just compute leading 28 bits of S once
	 * and for all and pass them and a shift to quorem, so it
	 * can do shifts and ors to compute the numerator for q.
	 */
#ifdef Pack_32
	if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0)
		i = 32 - i;
#else
	if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)
		i = 16 - i;
#endif
	if (i > 4) {
		i -= 4;
		b2 += i;
		m2 += i;
		s2 += i;
	}
	else if (i < 4) {
		i += 28;
		b2 += i;
		m2 += i;
		s2 += i;
	}
	if (b2 > 0)
		b = lshift(b, b2);
	if (s2 > 0)
		S = lshift(S, s2);
	if (k_check) {
		if (cmp(b,S) < 0) {
			k--;
			b = multadd(b, 10, 0);	/* we botched the k estimate */
			if (leftright)
				mhi = multadd(mhi, 10, 0);
			ilim = ilim1;
		}
	}
	if (ilim <= 0 && mode > 2) {
		if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
			/* no digits, fcvt style */
		no_digits:
			k = -1 - ndigits;
			goto ret;
		}
	one_digit:
		*s++ = '1';
		k++;
		goto ret;
	}
	if (leftright) {
		if (m2 > 0)
			mhi = lshift(mhi, m2);

		/* Compute mlo -- check for special case
		 * that d is a normalized power of 2.
		 */

		mlo = mhi;
		if (spec_case) {
			mhi = Balloc(mhi->k);
			Bcopy(mhi, mlo);
			mhi = lshift(mhi, Log2P);
		}

		for(i = 1;;i++) {
			dig = quorem(b,S) + '0';
			/* Do we yet have the shortest decimal string
			 * that will round to d?
			 */
			j = cmp(b, mlo);
			delta = diff(S, mhi);
			j1 = delta->sign ? 1 : cmp(b, delta);
			Bfree(delta);
#ifndef ROUND_BIASED
			if (j1 == 0 && !mode && !(word1(d) & 1)) {
				if (dig == '9')
					goto round_9_up;
				if (j > 0)
					dig++;
				*s++ = (char)dig;
				goto ret;
			}
#endif
			if ((j < 0) || ((j == 0) && (!mode)
#ifndef ROUND_BIASED
				&& (!(word1(d) & 1)))
#endif
				) {
				if (j1 > 0) {
					b = lshift(b, 1);
					j1 = cmp(b, S);
					if (((j1 > 0) || (j1 == 0 && dig & 1))
						&& (dig++ == '9'))
						goto round_9_up;
				}
				*s++ = (char)dig;
				goto ret;
			}
			if (j1 > 0) {
				if (dig == '9') { /* possible if i == 1 */
				round_9_up:
					*s++ = '9';
					goto roundoff;
				}
				*s++ = dig + 1;
				goto ret;
			}
			*s++ = (char)dig;
			if (i == ilim)
				break;
			b = multadd(b, 10, 0);
			if (mlo == mhi)
				mlo = mhi = multadd(mhi, 10, 0);
			else {
				mlo = multadd(mlo, 10, 0);
				mhi = multadd(mhi, 10, 0);
			}
		}
	}
	else
		for(i = 1;; i++) {
			*s++ = (char)(dig = quorem(b,S) + '0');
			if (i >= ilim)
				break;
			b = multadd(b, 10, 0);
		}

	/* Round off last digit */

	b = lshift(b, 1);
	j = cmp(b, S);
	if ((j > 0) || (j == 0 && dig & 1)) {
	roundoff:
		while(*--s == '9')
			if (s == s0) {
				k++;
				*s++ = '1';
				goto ret;
			}
		++*s++;
	}
	else {
		while(*--s == '0') ;
		s++;
	}
ret:
	Bfree(S);
	if (mhi) {
		if (mlo && mlo != mhi)
			Bfree(mlo);
		Bfree(mhi);
	}
ret1:
	Bfree(b);
	*s = 0;
	*decpt = k + 1;
	strsize = (s - s0) + 1;
	if (strsize <= bufsize) {
		retval = JS_TRUE;
		memcpy(buf, s0, strsize);
		if (rve) {
			*rve = buf + strsize - 1;
			JS_ASSERT(**rve == '\0');
		}
	} else {
		JS_ASSERT(JS_FALSE);
/* 		JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */
		retval = JS_FALSE;
	}

	/* cleanup */
	result->k = result_k;
	result->maxwds = 1 << result_k;
	Bfree(result);

	return retval;
}

/*
** conversion routines for floating point
** prcsn - number of digits of precision to generate floating
** point value.
** This should be reparameterized so that you can send in a
**   prcn for the positive and negative ranges.  For now, 
**   conform to the ECMA JavaScript spec which says numbers
**   less than 1e-6 are in scientific notation.
** Also, the ECMA spec says that there should always be a
**   '+' or '-' after the 'e' in scientific notation
*/
JS_FRIEND_API(void)
JS_cnvtf(char *buf,int bufsz, int prcsn,double fval)
{
    intN decpt,sign,numdigits;
    char *num, *nump;
    char *bufp = buf;
    char *endnum;

	/* If anything fails, we store an empty string in 'buf' */
	num = (char *)MALLOC(bufsz);
	if (num == NULL) {
		buf[0] = '\0';
		return;
	}
	/* XXX Why use mode 1? */
	if (JS_dtoa(fval,1,prcsn,&decpt,&sign,&endnum,num,bufsz)
			== JS_FALSE) {
		buf[0] = '\0';
		goto done;
	}
	numdigits = endnum - num;
	nump = num;

	/* If negative and not signed zero and not a NaN, print leading "-". */
	if (sign &&
	    !(word0(fval) == Sign_bit && word1(fval) == 0) &&
	    !((word0(fval) & Exp_mask) == Exp_mask &&
	      (word1(fval) || (word0(fval) & 0xfffff)))) {
	    *bufp++ = '-';
	}

	if(decpt == 9999){
		while((*bufp++ = *nump++) != 0) ;
		goto done;
	}

	if(decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5){
	    *bufp++ = *nump++;
	    if(numdigits != 1){
			*bufp++ = '.';
	    }

	    while(*nump != '\0'){
			*bufp++ = *nump++;
	    }
	    *bufp++ = 'e';
	    JS_snprintf(bufp,bufsz - (bufp - buf), "%+d",decpt-1);
	}
	else if(decpt >= 0){
	    if (decpt == 0){
			*bufp++ = '0';
	    }
	    else {
		while(decpt--){
			if(*nump != '\0'){
				*bufp++ = *nump++;
			}
			else {
				*bufp++ = '0';
			}
		}
	    }
	    if(*nump != '\0'){
			*bufp++ = '.';
			while(*nump != '\0'){
				*bufp++ = *nump++;
			}
	    }
	    *bufp++ = '\0';
	}
	else if(decpt < 0){
	    *bufp++ = '0';
	    *bufp++ = '.';
	    while(decpt++){
			*bufp++ = '0';
	    }
	    
	    while(*nump != '\0'){
			*bufp++ = *nump++;
	    }
	    *bufp++ = '\0';
	}
done:
        free(num);
}

**** End of jsdtoa.c. ****

**** Start of jsdtoa.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsdtoa_h___
#define jsdtoa_h___
/*
 * Public interface to portable double-precision floating point to string
 * and back conversion package.
 */

#include "jscompat.h"

JS_BEGIN_EXTERN_C

/*
 * JS_strtod() returns as a double-precision floating-point number
 * the  value represented by the character string pointed to by
 * s00.  The string is scanned up to  the  first  unrecognized
 * character.
 * If the value of se is not (char **)NULL,  a  pointer  to
 * the  character terminating the scan is returned in the location pointed
 * to by se.  If no number can be  formed, se is set to s00r, and
 * zero is returned.
 */
JS_FRIEND_API(double)
JS_strtod(const char *s00, char **se);

/*
 * JS_cnvtf()
 * conversion routines for floating point
 * prcsn - number of digits of precision to generate floating
 * point value.
 */
JS_FRIEND_API(void)
JS_cnvtf(char *buf, intN bufsz, intN prcsn, double dval);

JS_END_EXTERN_C

#endif /* jsdtoa_h___ */

**** End of jsdtoa.h. ****

**** Start of jsemit.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS bytecode generation.
 */
#include "jsstddef.h"
#include <memory.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsscript.h"

#define CGINCR  (256 * sizeof(jsbytecode))  /* code allocation increment */
#define SNINCR  (64 * sizeof(jssrcnote))    /* srcnote allocation increment */
#define TNINCR  (64 * sizeof(JSTryNote))    /* trynote allocation increment */

JS_FRIEND_API(JSBool)
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
		     const char *filename, uintN lineno,
		     JSPrincipals *principals)
{
    memset(cg, 0, sizeof *cg);
    cg->codeMark = JS_ARENA_MARK(&cx->codePool);
    cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
    JS_ARENA_ALLOCATE(cg->base, &cx->codePool, CGINCR);
    if (!cg->base) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
    cg->next = cg->base;
    cg->limit = CG_CODE(cg, CGINCR);
    cg->filename = filename;
    cg->firstLine = cg->currentLine = lineno;
    cg->principals = principals;
    TREE_CONTEXT_INIT(&cg->treeContext);
    ATOM_LIST_INIT(&cg->atomList);
    return JS_TRUE;
}

JS_FRIEND_API(void)
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
{
    JS_ARENA_RELEASE(&cx->codePool, cg->codeMark);
    JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);
}

static ptrdiff_t
EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)
{
    ptrdiff_t offset, length;
    size_t cgsize;

    JS_ASSERT(delta < CGINCR);
    offset = CG_OFFSET(cg);
    if ((jsuword)cg->next + delta >= (jsuword)cg->limit) {
	length = PTRDIFF(cg->limit, cg->base, jsbytecode);
	cgsize = length * sizeof(jsbytecode);
	JS_ARENA_GROW(cg->base, &cx->codePool, cgsize, CGINCR);
	if (!cg->base) {
	    JS_ReportOutOfMemory(cx);
	    return -1;
	}
	cg->limit = CG_CODE(cg, length + CGINCR);
	cg->next = CG_CODE(cg, offset);
    }
    return offset;
}

static void
UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
{
    jsbytecode *pc;
    JSCodeSpec *cs;
    intN nuses;

    pc = CG_CODE(cg, target);
    cs = &js_CodeSpec[pc[0]];
    nuses = cs->nuses;
    if (nuses < 0)
	nuses = 2 + GET_ARGC(pc);       /* stack: fun, this, [argc arguments] */
    cg->stackDepth -= nuses;
    if (cg->stackDepth < 0) {
	char numBuf[12];
	JS_snprintf(numBuf, sizeof numBuf, "%d", target);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_STACK_UNDERFLOW,
			     cg->filename ? cg->filename : "stdin", numBuf);
    }
    cg->stackDepth += cs->ndefs;
    if ((uintN)cg->stackDepth > cg->maxStackDepth)
	cg->maxStackDepth = cg->stackDepth;
}

ptrdiff_t
js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
{
    ptrdiff_t offset = EmitCheck(cx, cg, op, 1);

    if (offset >= 0) {
	*cg->next++ = (jsbytecode)op;
	UpdateDepth(cx, cg, offset);
    }
    return offset;
}

ptrdiff_t
js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)
{
    ptrdiff_t offset = EmitCheck(cx, cg, op, 2);

    if (offset >= 0) {
	cg->next[0] = (jsbytecode)op;
	cg->next[1] = op1;
	cg->next += 2;
	UpdateDepth(cx, cg, offset);
    }
    return offset;
}

ptrdiff_t
js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,
	 jsbytecode op2)
{
    ptrdiff_t offset = EmitCheck(cx, cg, op, 3);

    if (offset >= 0) {
	cg->next[0] = (jsbytecode)op;
	cg->next[1] = op1;
	cg->next[2] = op2;
	cg->next += 3;
	UpdateDepth(cx, cg, offset);
    }
    return offset;
}

ptrdiff_t
js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
{
    ptrdiff_t length = 1 + (ptrdiff_t)extra;
    ptrdiff_t offset = EmitCheck(cx, cg, op, length);

    if (offset >= 0) {
	*cg->next = (jsbytecode)op;
	cg->next += length;
	UpdateDepth(cx, cg, offset);
    }
    return offset;
}

static const char *statementName[] = {
    "block",             /* BLOCK */
    "label statement",   /* LABEL */
    "if statement",      /* IF */
    "else statement",    /* ELSE */
    "switch statement",  /* SWITCH */
    "with statement",    /* WITH */
    "try statement",     /* TRY */
    "catch block",       /* CATCH */
    "finally statement", /* FINALLY */
    "do loop",           /* DO_LOOP */
    "for loop",          /* FOR_LOOP */
    "for/in loop",       /* FOR_IN_LOOP */
    "while loop",        /* WHILE_LOOP */
};

static const char *
StatementName(JSCodeGenerator *cg)
{
    if (!cg->treeContext.topStmt)
	return "script";
    return statementName[cg->treeContext.topStmt->type];
}

static void
ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)
{
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
			 StatementName(cg));
}

JSBool
js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
		 ptrdiff_t off)
{
    if (off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off) {
	ReportStatementTooLarge(cx, cg);
	return JS_FALSE;
    }
    SET_JUMP_OFFSET(pc, off);
    return JS_TRUE;
}

void
js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
		 ptrdiff_t top)
{
    stmt->type = type;
    SET_STATEMENT_TOP(stmt, top);
    stmt->label = NULL;
    stmt->down = tc->topStmt;
    tc->topStmt = stmt;
}

static ptrdiff_t
EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
	 ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType)
{
    JSStmtInfo *stmt;
    intN index;
    uint16 finallyIndex = 0;
    ptrdiff_t offset, delta;

    for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
	switch (stmt->type) {
	  case STMT_FINALLY:
	    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		js_Emit3(cx, cg, JSOP_GOSUB, JUMP_OFFSET_HI(finallyIndex),
			 JUMP_OFFSET_LO(finallyIndex)) < 0) {
		return -1;
	    }
	    finallyIndex--;
	    break;
	  case STMT_WITH:
	    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
		return -1;
	    cg->stackDepth++;
	    if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
		return -1;
	    break;
	  case STMT_FOR_IN_LOOP:
	    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
		return -1;
	    cg->stackDepth += 2;
	    if (js_Emit1(cx, cg, JSOP_POP2) < 0)
		return -1;
	    break;
	  default:;
	}
    }

    if (label) {
	index = js_NewSrcNote(cx, cg, noteType);
	if (index < 0)
	    return -1;
	if (!js_SetSrcNoteOffset(cx, cg, (uintN)index, 0,
				 (ptrdiff_t)label->index)) {
	    return -1;
	}
    }

    offset = CG_OFFSET(cg);
    delta = offset - *last;
    *last = offset;
    return js_Emit3(cx, cg, JSOP_GOTO,
		    JUMP_OFFSET_HI(delta), JUMP_OFFSET_LO(delta));
}

static JSBool
PatchGotos(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
	   ptrdiff_t last, jsbytecode *target)
{
    jsbytecode *pc, *top;
    ptrdiff_t delta, jumpOffset;

    pc = CG_CODE(cg, last);
    top = CG_CODE(cg, stmt->top);
    while (pc != CG_CODE(cg, -1)) {
	JS_ASSERT(*pc == JSOP_GOTO);
	delta = GET_JUMP_OFFSET(pc);
	jumpOffset = PTRDIFF(target, pc, jsbytecode);
	CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, jumpOffset);
	pc -= delta;
    }
    return JS_TRUE;
}

ptrdiff_t
js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
	     JSAtomListElement *label)
{
    return EmitGoto(cx, cg, stmt, &stmt->breaks, label, SRC_BREAK2LABEL);
}

ptrdiff_t
js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
		JSAtomListElement *label)
{
    return EmitGoto(cx, cg, stmt, &stmt->continues, label, SRC_CONT2LABEL);
}

extern void
js_PopStatement(JSTreeContext *tc)
{
    tc->topStmt = tc->topStmt->down;
}

JSBool
js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
{
    JSStmtInfo *stmt;

    stmt = cg->treeContext.topStmt;
    if (!PatchGotos(cx, cg, stmt, stmt->breaks, cg->next))
	return JS_FALSE;
    if (!PatchGotos(cx, cg, stmt, stmt->continues, CG_CODE(cg, stmt->update)))
	return JS_FALSE;
    cg->treeContext.topStmt = stmt->down;
    return JS_TRUE;
}

/*
 * Emit a bytecode and its 2-byte constant (atom) index immediate operand.
 * NB: We use cx and cg from our caller's lexical environment, and return
 * false on error.
 */
#define EMIT_ATOM_INDEX_OP(op, atomIndex)                                     \
    JS_BEGIN_MACRO                                                            \
	if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex),                    \
				 ATOM_INDEX_LO(atomIndex)) < 0) {             \
	    return JS_FALSE;                                                  \
	}                                                                     \
    JS_END_MACRO

static JSBool
EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
{
    JSAtomListElement *ale;

    ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
    if (!ale)
	return JS_FALSE;
    EMIT_ATOM_INDEX_OP(op, ale->index);
    return JS_TRUE;
}

static JSBool
EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
{
    JSParseNode *pn2;
    JSAtomListElement *ale;

    pn2 = pn->pn_expr;
    if (!js_EmitTree(cx, cg, pn2))
	return JS_FALSE;
    if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
		       (ptrdiff_t)(CG_OFFSET(cg) - pn2->pn_offset)) < 0) {
	return JS_FALSE;
    }
    if (!pn->pn_atom) {
	JS_ASSERT(op == JSOP_IMPORTALL);
	if (js_Emit1(cx, cg, op) < 0)
	    return JS_FALSE;
    } else {
	ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
	if (!ale)
	    return JS_FALSE;
	EMIT_ATOM_INDEX_OP(op, ale->index);
    }
    return JS_TRUE;
}

static JSBool
EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
{
    JSParseNode *pn2;

    pn2 = pn->pn_left;
    if (!js_EmitTree(cx, cg, pn2) || !js_EmitTree(cx, cg, pn->pn_right))
	return JS_FALSE;
    if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
		       (ptrdiff_t)(CG_OFFSET(cg) - pn2->pn_offset)) < 0) {
	return JS_FALSE;
    }
    return js_Emit1(cx, cg, op) >= 0;
}

static JSBool
EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
{
    jsint ival;
    jsatomid atomIndex;
    JSAtom *atom;
    JSAtomListElement *ale;

    if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
	if (ival == 0)
	    return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
	if (ival == 1)
	    return js_Emit1(cx, cg, JSOP_ONE) >= 0;
	if ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) {
	    atomIndex = (jsatomid)ival;
	    EMIT_ATOM_INDEX_OP(JSOP_UINT16, atomIndex);
	    return JS_TRUE;
	}
	atom = js_AtomizeInt(cx, ival, 0);
    } else {
	atom = js_AtomizeDouble(cx, dval, 0);
    }
    if (!atom)
	return JS_FALSE;
    ale = js_IndexAtom(cx, atom, &cg->atomList);
    if (!ale)
	return JS_FALSE;
    EMIT_ATOM_INDEX_OP(JSOP_NUMBER, ale->index);
    return JS_TRUE;
}

JSBool
js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
		    JSFunction *fun)
{
    if (!js_AllocTryNotes(cx, cg))
	return JS_FALSE;
    if (!js_EmitTree(cx, cg, body))
	return JS_FALSE;
    fun->script = js_NewScriptFromCG(cx, cg, fun);
    if (!fun->script)
	return JS_FALSE;
    return JS_TRUE;
}

#if JS_HAS_EXCEPTIONS

/* XXX use PatchGotos-style back-patch chaining, not bytecode-linear search */
#define BYTECODE_ITER(pc, max, body) \
    while (pc < max) {                                                        \
	JSCodeSpec *cs = &js_CodeSpec[(JSOp)*pc];                             \
	body;                                                                 \
	if ((cs->format & JOF_TYPEMASK) == JOF_TABLESWITCH ||                 \
	    (cs->format & JOF_TYPEMASK) == JOF_LOOKUPSWITCH) {                \
	    pc += GET_JUMP_OFFSET(pc);                                        \
	} else {                                                              \
	    pc += cs->length;                                                 \
	}                                                                     \
    }

static JSBool
FixupFinallyJumps(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t tryStart,
		  ptrdiff_t finallyIndex)
{
    jsbytecode *pc;

    pc = cg->base + tryStart;
    BYTECODE_ITER(pc, cg->next,
	if (*pc == JSOP_GOSUB) {
	    ptrdiff_t index = GET_JUMP_OFFSET(pc);
	    if (index <= 0) {
		if (index == 0)
		    index = finallyIndex - (pc - cg->base);
		else
		    index++;
		CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, index);
	    }
	}
    );
    return JS_TRUE;
}

static JSBool
FixupCatchJumps(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t tryStart,
		ptrdiff_t postCatch)
{
    jsbytecode *pc;

    pc = cg->base + tryStart;
    BYTECODE_ITER(pc, cg->next,
	if (*pc == JSOP_GOTO && !GET_JUMP_OFFSET(pc)) {
	    CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, postCatch - (pc - cg->base));
	}
    );
    return JS_TRUE;
}

#endif /* JS_HAS_EXCEPTIONS */


/* a macro for inlining at the top of js_EmitTree (from whence it came) */
#define UPDATE_LINENO_NOTES(cx, cg, pn)                                     \
    JS_BEGIN_MACRO                                                          \
    uintN lineno, delta;                                                    \
    lineno = pn->pn_pos.begin.lineno;                                       \
    delta = lineno - cg->currentLine;                                       \
    cg->currentLine = lineno;                                               \
    if (delta) {                                                            \
	/*                                                                  \
	 * Encode any change in the current source line number by using     \
	 * either several SRC_NEWLINE notes or one SRC_SETLINE note,        \
	 * whichever consumes less space.                                   \
	 */                                                                 \
	if (delta >= (uintN)(2 + ((lineno > SN_3BYTE_OFFSET_MASK) << 1))) { \
	    if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)lineno) < 0) \
		return JS_FALSE;                                            \
	} else {                                                            \
	    do {                                                            \
		if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)                 \
		    return JS_FALSE;                                        \
	    } while (--delta != 0);                                         \
	}                                                                   \
    }                                                                       \
    JS_END_MACRO

/* a function so that we can make the (few) less frequent calls */
static JSBool
UpdateLinenoNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
    UPDATE_LINENO_NOTES(cx, cg, pn);
    return JS_TRUE;
}

JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
    JSBool ok;
    JSCodeGenerator cg2;
    JSStmtInfo *stmt, stmtInfo;
    ptrdiff_t top, off, tmp, beq, jmp;
    JSParseNode *pn2, *pn3, *pn4;
    JSAtom *atom;
    JSAtomListElement *ale;
    jsatomid atomIndex;
    intN noteIndex;
    JSOp op;
    uint32 argc;

    pn->pn_offset = top = CG_OFFSET(cg);

    /* Emit notes to tell the current bytecode's source line number. */
    UPDATE_LINENO_NOTES(cx, cg, pn);

    switch (pn->pn_type) {
      case TOK_FUNCTION:
      {
	JSFunction *fun;

	/* Fold constants and generate code for the function's body. */
	pn2 = pn->pn_body;
	if (!js_FoldConstants(cx, pn2))
	    return JS_FALSE;
	if (!js_InitCodeGenerator(cx, &cg2, cg->filename,
				  pn->pn_pos.begin.lineno,
				  cg->principals)) {
	    return JS_FALSE;
	}
	cg2.treeContext.tryCount = pn->pn_tryCount;
	fun = pn->pn_fun;
	if (!js_EmitFunctionBody(cx, &cg2, pn2, fun))
	    return JS_FALSE;
	js_FinishCodeGenerator(cx, &cg2);

	/* Make the function object a literal in the outer script's pool. */
	atom = js_AtomizeObject(cx, fun->object, 0);
	if (!atom)
	    return JS_FALSE;
	ale = js_IndexAtom(cx, atom, &cg->atomList);
	if (!ale)
	    return JS_FALSE;

	/* Emit a bytecode or srcnote naming the literal in its immediate. */
#if JS_HAS_LEXICAL_CLOSURE
	if (pn->pn_op != JSOP_NOP) {
	    EMIT_ATOM_INDEX_OP(pn->pn_op, ale->index);
	    break;
	}
#endif
	noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)ale->index);
	if (noteIndex < 0 ||
	    js_Emit1(cx, cg, JSOP_NOP) < 0) {
	    return JS_FALSE;
	}
	break;
      }

#if JS_HAS_EXPORT_IMPORT
      case TOK_EXPORT:
	pn2 = pn->pn_head;
	if (pn2->pn_type == TOK_STAR) {
	    /*
	     * 'export *' must have no other elements in the list (what would
	     * be the point?).
	     */
	    if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0)
		return JS_FALSE;
	} else {
	    /*
	     * If not 'export *', the list consists of NAME nodes identifying
	     * properties of the variable object to flag as exported.
	     */
	    do {
		ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
		if (!ale)
		    return JS_FALSE;
		EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ale->index);
	    } while ((pn2 = pn2->pn_next) != NULL);
	}
	break;

      case TOK_IMPORT:
	for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
	    /*
	     * Each subtree on an import list is rooted by a DOT or LB node.
	     * A DOT may have a null pn_atom member, in which case pn_op must
	     * be JSOP_IMPORTALL -- see EmitPropOp above.
	     */
	    if (!js_EmitTree(cx, cg, pn2))
		return JS_FALSE;
	}
	break;
#endif /* JS_HAS_EXPORT_IMPORT */

      case TOK_IF:
	/* Emit code for the condition before pushing stmtInfo. */
	if (!js_EmitTree(cx, cg, pn->pn_kid1))
	    return JS_FALSE;
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, CG_OFFSET(cg));

	/* Emit an annotated branch-if-false around the then part. */
	noteIndex = js_NewSrcNote(cx, cg, SRC_IF);
	if (noteIndex < 0)
	    return JS_FALSE;
	beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
	if (beq < 0)
	    return JS_FALSE;

	/* Emit code for the then and optional else parts. */
	if (!js_EmitTree(cx, cg, pn->pn_kid2))
	    return JS_FALSE;
	pn3 = pn->pn_kid3;
	if (pn3) {
	    /* Modify stmtInfo and the branch-if-false source note. */
	    stmtInfo.type = STMT_ELSE;
	    SN_SET_TYPE(&cg->notes[noteIndex], SRC_IF_ELSE);

	    /* Jump at end of then part around the else part. */
	    jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	    if (jmp < 0)
		return JS_FALSE;

	    /* Ensure the branch-if-false comes here, then emit the else. */
	    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
	    if (!js_EmitTree(cx, cg, pn3))
		return JS_FALSE;

	    /* Fixup the jump around the else part. */
	    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
	} else {
	    /* No else part, fixup the branch-if-false to come here. */
	    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
	}
	return js_PopStatementCG(cx, cg);

#if JS_HAS_SWITCH_STATEMENT
      case TOK_SWITCH:
      {
	JSOp switchop;
	uint32 ncases, tablen = 0;
	JSScript *script;
	jsint i, low, high;
	jsdouble d;
	size_t switchsize, tablesize;
	void *mark;
	JSParseNode **table;
	jsbytecode *pc;
	JSBool hasDefault = JS_FALSE;
	JSBool isEcmaSwitch = cx->version == JSVERSION_DEFAULT ||
			      cx->version >= JSVERSION_1_4;
	ptrdiff_t defaultOffset = -1;

	/* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
	switchop = JSOP_TABLESWITCH;

	/* Emit code for the discriminant first. */
	if (!js_EmitTree(cx, cg, pn->pn_kid1))
	    return JS_FALSE;

	/* Switch bytecodes run from here till end of final case. */
	top = CG_OFFSET(cg);
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_SWITCH, top);

	pn2 = pn->pn_kid2;
	ncases = pn2->pn_count;

	if (pn2->pn_count == 0) {
	    low = high = 0;
	    tablen = 0;
	    ok = JS_TRUE;
	} else {
	    low  = JSVAL_INT_MAX;
	    high = JSVAL_INT_MIN;
	    cg2.base = NULL;
	    for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
		if (pn3->pn_type == TOK_DEFAULT) {
		    hasDefault = JS_TRUE;
		    ncases--;   /* one of the "cases" was the default */
		    continue;
		}
		JS_ASSERT(pn3->pn_type == TOK_CASE);
		pn4 = pn3->pn_left;
		if (isEcmaSwitch) {
		    if (switchop == JSOP_CONDSWITCH)
			continue;
		    switch (pn4->pn_type) {
		      case TOK_NUMBER:
			d = pn4->pn_dval;
			if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
			    pn3->pn_val = INT_TO_JSVAL(i);
			} else {
			    atom = js_AtomizeDouble(cx, d, 0);
			    if (!atom)
				return JS_FALSE;
			    pn3->pn_val = ATOM_KEY(atom);
			}
			break;
		      case TOK_STRING:
			pn3->pn_val = ATOM_KEY(pn4->pn_atom);
			break;
		      case TOK_PRIMARY:
			if (pn4->pn_op == JSOP_TRUE) {
			    pn3->pn_val = JSVAL_TRUE;
			    break;
			}
			if (pn4->pn_op == JSOP_FALSE) {
			    pn3->pn_val = JSVAL_FALSE;
			    break;
			}
			/* FALL THROUGH */
		      default:
			switchop = JSOP_CONDSWITCH;
			continue;
		    }
		} else {
		    /* Pre-ECMAv2 switch evals case exprs at compile time. */
		    if (!js_InitCodeGenerator(cx, &cg2, cg->filename,
					      pn3->pn_pos.begin.lineno,
					      cg->principals)) {
			return JS_FALSE;
		    }
		    cg2.currentLine = pn4->pn_pos.begin.lineno;
		    if (!js_EmitTree(cx, &cg2, pn4))
			return JS_FALSE;
		    if (js_Emit1(cx, &cg2, JSOP_POPV) < 0)
			return JS_FALSE;
		    script = js_NewScriptFromCG(cx, &cg2, NULL);
		    if (!script)
			return JS_FALSE;
		    ok = js_Execute(cx, cx->fp->scopeChain, script, NULL,
				    cx->fp, JS_FALSE, &pn3->pn_val);
		    js_DestroyScript(cx, script);
		    if (!ok)
			return JS_FALSE;
		}

		if (!JSVAL_IS_NUMBER(pn3->pn_val) &&
		    !JSVAL_IS_STRING(pn3->pn_val) &&
		    !JSVAL_IS_BOOLEAN(pn3->pn_val)) {
		    char numBuf[12];
		    JS_snprintf(numBuf, sizeof numBuf, "%u",
		    		pn4->pn_pos.begin.lineno);
		    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
					 JSMSG_BAD_CASE,
					 cg2.filename ? cg2.filename : "stdin",
					 numBuf);
		    return JS_FALSE;
		}

		if (switchop != JSOP_TABLESWITCH)
		    continue;
		if (!JSVAL_IS_INT(pn3->pn_val)) {
		    switchop = JSOP_LOOKUPSWITCH;
		    continue;
		}
		i = JSVAL_TO_INT(pn3->pn_val);
		if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) {
		    switchop = JSOP_LOOKUPSWITCH;
		    continue;
		}
		if (i < low)
		    low = i;
		if (high < i)
		    high = i;
	    }
	    if (switchop == JSOP_CONDSWITCH) {
		JS_ASSERT(!cg2.base);
	    } else {
		if (cg2.base)
		    js_FinishCodeGenerator(cx, &cg2);
		if (switchop == JSOP_TABLESWITCH) {
		    tablen = (uint32)(high - low + 1);
		    if (tablen >= JS_BIT(16) || tablen > 2 * ncases)
			switchop = JSOP_LOOKUPSWITCH;
		}
	    }
	}

	/*
	 * Emit a note with two offsets: first tells total switch code length,
	 * second tells offset to first JSOP_CASE if condswitch.
	 */
	noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0);
	if (noteIndex < 0)
	    return JS_FALSE;

	if (switchop == JSOP_CONDSWITCH) {
	    /*
	     * 0 bytes of immediate for unoptimized ECMAv2 switch.
	     */
	    switchsize = 0;
	} else if (switchop == JSOP_TABLESWITCH) {
	    /*
	     * 3 offsets (len, low, high) before the table, 1 per entry.
	     */
	    switchsize = (size_t)(6 + 2 * tablen);
	} else {
	    /*
	     * JSOP_LOOKUPSWITCH:
	     * 1 offset (len) and 1 atom index (npairs) before the table,
	     * 1 atom index and 1 jump offset per entry.
	     */
	    switchsize = (size_t)(4 + 4 * ncases);
	}

	/* Emit switchop and switchsize bytes of jump or lookup table. */
	if (js_EmitN(cx, cg, switchop, switchsize) < 0)
	    return JS_FALSE;

        off = -1;
	if (switchop == JSOP_CONDSWITCH) {
	    intN caseNoteIndex = -1;

	    /* Emit code for evaluating cases and jumping to case statements. */
	    for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
		pn4 = pn3->pn_left;
		if (pn4 && !js_EmitTree(cx, cg, pn4))
		    return JS_FALSE;
		if (caseNoteIndex >= 0) {
		    /* off is the previous JSOP_CASE's bytecode offset. */
		    if (!js_SetSrcNoteOffset(cx, cg, caseNoteIndex, 0,
					     CG_OFFSET(cg) - off)) {
			return JS_FALSE;
		    }
		}
		if (pn3->pn_type == TOK_DEFAULT)
		    continue;
		caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
		if (caseNoteIndex < 0)
		    return JS_FALSE;
		off = js_Emit3(cx, cg, JSOP_CASE, 0, 0);
		if (off < 0)
		    return JS_FALSE;
		pn3->pn_offset = off;
		if (pn3 == pn2->pn_head) {
		    /* Switch note's second offset is to first JSOP_CASE. */
		    if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, off - top))
			return JS_FALSE;
		}
	    }

	    /* Emit default even if no explicit default statement. */
	    defaultOffset = js_Emit3(cx, cg, JSOP_DEFAULT, 0, 0);
	    if (defaultOffset < 0)
		return JS_FALSE;
	}

	/* Emit code for each case's statements, copying pn_offset up to pn3. */
	for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
	    if (switchop == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) {
		pn3->pn_val = INT_TO_JSVAL(pn3->pn_offset - top);
		CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset);
	    }
	    pn4 = pn3->pn_right;
	    if (!js_EmitTree(cx, cg, pn4))
		return JS_FALSE;
	    pn3->pn_offset = pn4->pn_offset;
	    if (pn3->pn_type == TOK_DEFAULT)
		off = pn3->pn_offset - top;
	}

	if (!hasDefault) {
	    /* If no default case, offset for default is to end of switch. */
	    off = CG_OFFSET(cg) - top;
	}

        /* We better have set "off" by now. */
        JS_ASSERT(off != -1);

	/* Set the default offset (to end of switch if no default). */
        pc = NULL;
	if (switchop == JSOP_CONDSWITCH) {
	    JS_ASSERT(defaultOffset != -1);
	    if (!js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset),
				  off - (defaultOffset - top))) {
		return JS_FALSE;
	    }
	} else {
	    pc = CG_CODE(cg, top);
	    if (!js_SetJumpOffset(cx, cg, pc, off))
		return JS_FALSE;
	    pc += 2;
	}

	/* Set the SRC_SWITCH note's offset operand to tell end of switch. */
	off = CG_OFFSET(cg) - top;
	if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, off))
	    return JS_FALSE;

	if (switchop == JSOP_TABLESWITCH) {
	    /* Fill in jump table. */
	    if (!js_SetJumpOffset(cx, cg, pc, low))
		return JS_FALSE;
	    pc += 2;
	    if (!js_SetJumpOffset(cx, cg, pc, high))
		return JS_FALSE;
	    pc += 2;
	    if (tablen) {
		/* Avoid bloat for a compilation unit with many switches. */
		mark = JS_ARENA_MARK(&cx->tempPool);
		tablesize = (size_t)tablen * sizeof *table;
		JS_ARENA_ALLOCATE(table, &cx->tempPool, tablesize);
		if (!table) {
		    JS_ReportOutOfMemory(cx);
		    return JS_FALSE;
		}
		memset(table, 0, tablesize);
		for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
		    if (pn3->pn_type == TOK_DEFAULT)
			continue;
		    i = JSVAL_TO_INT(pn3->pn_val);
		    i -= low;
		    JS_ASSERT((uint32)i < tablen);
		    table[i] = pn3;
		}
		for (i = 0; i < (jsint)tablen; i++) {
		    pn3 = table[i];
		    off = pn3 ? pn3->pn_offset - top : 0;
		    if (!js_SetJumpOffset(cx, cg, pc, off))
			return JS_FALSE;
		    pc += 2;
		}
		JS_ARENA_RELEASE(&cx->tempPool, mark);
	    }
	} else if (switchop == JSOP_LOOKUPSWITCH) {
	    /* Fill in lookup table. */
	    SET_ATOM_INDEX(pc, ncases);
	    pc += 2;

	    for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
		if (pn3->pn_type == TOK_DEFAULT)
		    continue;
		atom = js_AtomizeValue(cx, pn3->pn_val, 0);
		if (!atom)
		    return JS_FALSE;
		ale = js_IndexAtom(cx, atom, &cg->atomList);
		if (!ale)
		    return JS_FALSE;
		SET_ATOM_INDEX(pc, ale->index);
		pc += 2;

		off = pn3->pn_offset - top;
		if (!js_SetJumpOffset(cx, cg, pc, off))
		    return JS_FALSE;
		pc += 2;
	    }
	}

	return js_PopStatementCG(cx, cg);
      }
#endif /* JS_HAS_SWITCH_STATEMENT */

      case TOK_WHILE:
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
	if (!js_EmitTree(cx, cg, pn->pn_left))
	    return JS_FALSE;
	noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
	if (noteIndex < 0)
	    return JS_FALSE;
	beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
	if (beq < 0)
	    return JS_FALSE;
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;
	jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	if (jmp < 0)
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp);
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
	return js_PopStatementCG(cx, cg);

#if JS_HAS_DO_WHILE_LOOP
      case TOK_DO:
	/* Emit an annotated nop so we know to decompile a 'do' keyword. */
	if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||
	    js_Emit1(cx, cg, JSOP_NOP) < 0) {
	    return JS_FALSE;
	}

	/* Compile the loop body. */
	top = CG_OFFSET(cg);
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top);
	if (!js_EmitTree(cx, cg, pn->pn_left))
	    return JS_FALSE;

	/* Set loop and enclosing label update offsets, for continue. */
	stmt = &stmtInfo;
	do {
	    stmt->update = CG_OFFSET(cg);
	} while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);

	/* Compile the loop condition, now that continues know where to go. */
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;

	/* Re-use the SRC_WHILE note, this time for the JSOP_IFNE opcode. */
	if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0)
	    return JS_FALSE;
	jmp = top - CG_OFFSET(cg);
	if (js_Emit3(cx, cg, JSOP_IFNE,
		     JUMP_OFFSET_HI(jmp), JUMP_OFFSET_LO(jmp)) < 0) {
	    return JS_FALSE;
	}
	return js_PopStatementCG(cx, cg);
#endif /* JS_HAS_DO_WHILE_LOOP */

      case TOK_FOR:
	pn2 = pn->pn_left;
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);

	if (pn2->pn_type == TOK_IN) {
	    /* If the left part is var x = i, bind x, evaluate i, and pop. */
	    pn3 = pn2->pn_left;
	    if (pn3->pn_type == TOK_VAR && pn3->pn_head->pn_expr) {
		if (!js_EmitTree(cx, cg, pn3))
		    return JS_FALSE;
		/* Set pn3 to the variable name, to avoid another var note. */
		pn3 = pn3->pn_head;
		JS_ASSERT(pn3->pn_type == TOK_NAME);
	    }

	    /* Fix stmtInfo and emit a push to allocate the iterator. */
	    stmtInfo.type = STMT_FOR_IN_LOOP;
	    noteIndex = -1;
	    if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
		return JS_FALSE;

	    /* Compile the object expression to the right of 'in'. */
	    if (!js_EmitTree(cx, cg, pn2->pn_right))
		return JS_FALSE;
	    if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0)
		return JS_FALSE;

	    top = CG_OFFSET(cg);
	    SET_STATEMENT_TOP(&stmtInfo, top);

	    /* Compile a JSOP_FOR*2 bytecode based on the left hand side. */
	    switch (pn3->pn_type) {
	      case TOK_VAR:
		pn3 = pn3->pn_head;
		if (js_NewSrcNote(cx, cg, SRC_VAR) < 0)
		    return JS_FALSE;
		/* FALL THROUGH */
	      case TOK_NAME:
		if (!EmitAtomOp(cx, pn3, JSOP_FORNAME2, cg))
		    return JS_FALSE;
		break;
	      case TOK_DOT:
		if (!EmitPropOp(cx, pn3, JSOP_FORPROP2, cg))
		    return JS_FALSE;
		break;
	      case TOK_LB:
		if (!EmitElemOp(cx, pn3, JSOP_FORELEM2, cg))
		    return JS_FALSE;
		break;
	      default:
		JS_ASSERT(0);
	    }

	    /* Pop and test the loop condition generated by JSOP_FOR*2. */
	    beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
	    if (beq < 0)
		return JS_FALSE;
	} else {
	    if (!pn2->pn_kid1) {
		/* No initializer: emit an annotated nop for the decompiler. */
		noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
		if (noteIndex < 0 ||
		    js_Emit1(cx, cg, JSOP_NOP) < 0) {
		    return JS_FALSE;
		}
	    } else {
		if (!js_EmitTree(cx, cg, pn2->pn_kid1))
		    return JS_FALSE;
		noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
		if (noteIndex < 0 ||
		    js_Emit1(cx, cg, JSOP_POP) < 0) {
		    return JS_FALSE;
		}
	    }

	    top = CG_OFFSET(cg);
	    SET_STATEMENT_TOP(&stmtInfo, top);
	    if (!pn2->pn_kid2) {
		/* No loop condition: flag this fact in the source notes. */
		if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, 0))
		    return JS_FALSE;
		beq = 0;
	    } else {
		if (!js_EmitTree(cx, cg, pn2->pn_kid2))
		    return JS_FALSE;
		if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0,
					 (ptrdiff_t)(CG_OFFSET(cg) - top))) {
		    return JS_FALSE;
		}
		beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
		if (beq < 0)
		    return JS_FALSE;
	    }
	}

	/* Emit code for the loop body. */
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;

	if (pn2->pn_type != TOK_IN) {
	    /* Set the second note offset so we can find the update part. */
	    JS_ASSERT(noteIndex != -1);
	    if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1,
				     (ptrdiff_t)(CG_OFFSET(cg) - top))) {
		return JS_FALSE;
	    }

	    pn3 = pn2->pn_kid3;
	    if (pn3) {
		/* Set loop and enclosing "update" offsets, for continue. */
		stmt = &stmtInfo;
		do {
		    stmt->update = CG_OFFSET(cg);
		} while ((stmt = stmt->down) != NULL &&
			 stmt->type == STMT_LABEL);

		if (!js_EmitTree(cx, cg, pn3))
		    return JS_FALSE;
		if (js_Emit1(cx, cg, JSOP_POP) < 0)
		    return JS_FALSE;

		/* Restore the absolute line number for source note readers. */
		off = (ptrdiff_t) pn->pn_pos.end.lineno;
		if (cg->currentLine != (uintN) off) {
		    if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
			return JS_FALSE;
		    cg->currentLine = (uintN) off;
		}
	    }

	    /* The third note offset helps us find the loop-closing jump. */
	    if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 2,
				     (ptrdiff_t)(CG_OFFSET(cg) - top))) {
		return JS_FALSE;
	    }
	}

	/* Emit the loop-closing jump and fixup all jump offsets. */
	jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	if (jmp < 0)
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp);
	if (beq > 0)
	    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

	/* Now fixup all breaks and continues (before for/in's final POP2). */
	if (!js_PopStatementCG(cx, cg))
	    return JS_FALSE;

	if (pn2->pn_type == TOK_IN) {
	    /*
	     * Generate the object and iterator pop opcodes after popping the
	     * stmtInfo stack, so breaks will go to this pop bytecode.
	     */
	    if (js_Emit1(cx, cg, JSOP_POP2) < 0)
		return JS_FALSE;
	}
	break;

      case TOK_BREAK:
	stmt = cg->treeContext.topStmt;
	atom = pn->pn_atom;
	if (atom) {
	    ale = js_IndexAtom(cx, atom, &cg->atomList);
	    if (!ale)
		return JS_FALSE;
	    while (stmt->type != STMT_LABEL || stmt->label != atom)
		stmt = stmt->down;
	} else {
	    ale = NULL;
	    while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
		stmt = stmt->down;
	}
	if (js_EmitBreak(cx, cg, stmt, ale) < 0)
	    return JS_FALSE;
	break;

      case TOK_CONTINUE:
	stmt = cg->treeContext.topStmt;
	atom = pn->pn_atom;
	if (atom) {
            /* Find the loop statement enclosed by the matching label. */
            JSStmtInfo *loop = NULL;
	    ale = js_IndexAtom(cx, atom, &cg->atomList);
	    if (!ale)
		return JS_FALSE;
            while (stmt->type != STMT_LABEL || stmt->label != atom) {
                if (STMT_IS_LOOP(stmt))
                    loop = stmt;
		stmt = stmt->down;
            }
            stmt = loop;
	} else {
	    ale = NULL;
	    while (!STMT_IS_LOOP(stmt))
		stmt = stmt->down;
	    if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
		return JS_FALSE;
	}
	if (js_EmitContinue(cx, cg, stmt, ale) < 0)
	    return JS_FALSE;
	break;

      case TOK_WITH:
	if (!js_EmitTree(cx, cg, pn->pn_left))
	    return JS_FALSE;
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg));
	if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
	    return JS_FALSE;
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;
	if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
	    return JS_FALSE;
	return js_PopStatementCG(cx, cg);

#if JS_HAS_EXCEPTIONS

      case TOK_TRY: {
	ptrdiff_t start, end;
        ptrdiff_t catchStart = -1, finallyCatch = -1, catchjmp = -1;
	JSParseNode *iter = pn;
	uint16 depth;

	/* XXX use -(CG_OFFSET + 1) */
#define EMIT_FINALLY_GOSUB(cx, cg)                                            \
    JS_BEGIN_MACRO                                                            \
	if (!js_Emit3(cx, cg, JSOP_GOSUB, 0, 0))                              \
	    return JS_FALSE;                                                  \
    JS_END_MACRO

	/*
	 * When a finally block is `active' (STMT_FINALLY on the treeContext),
	 * non-local jumps result in a GOSUB being written into the bytecode
	 * stream for later fixup.  The GOSUB is written with offset 0 for the
	 * innermost finally, -1 for the next, etc.  As the finally fixup code
	 * runs for each finished try/finally, it will fix the GOSUBs with
	 * offset 0 to match the appropriate finally code for its block
	 * and decrement all others by one.
	 *
	 * NOTE: This will cause problems if we use GOSUBs for something other
	 * than finally handling in the future.  Caveat hacker!
	 */
	if (pn->pn_kid3) {
	    js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FINALLY,
			     CG_OFFSET(cg));
	}

	/*
	 * About SETSP:
	 * An exception can be thrown while the stack is in an unbalanced
	 * state, and this causes problems with things like function invocation
	 * later on.
	 *
	 * To fix this, we compute the `balanced' stack depth upon try entry,
	 * and then restore the stack to this depth when we hit the first catch
	 * or finally block.  We can't just zero the stack, because things like
	 * for/in and with that are active upon entry to the block keep things
	 * on the stack.
	 */
	depth = cg->stackDepth;

	/* mark try location for decompilation, then emit try block */
	if (js_NewSrcNote2(cx, cg, SRC_TRYFIN, 0) < 0 ||
	    js_Emit1(cx, cg, JSOP_NOP) < 0)
	    return JS_FALSE;
	start = CG_OFFSET(cg);
	if(!js_EmitTree(cx, cg, pn->pn_kid1))
	    return JS_FALSE;

	/* emit (hidden) jump over catch and/or finally */
	if (pn->pn_kid3) {
	    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
		return JS_FALSE;
	    EMIT_FINALLY_GOSUB(cx, cg);
	}
	if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
	    return JS_FALSE;
	jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	if (jmp < 0)
	    return JS_FALSE;
	end = CG_OFFSET(cg);

	/* if this try has a catch block, emit it */
	if (pn->pn_kid2) {
	    catchStart = end;
	    /*
	     * The emitted code for a catch block looks like:
	     *
	     * [ popscope ]                       only if 2nd+ catch block
	     * name Object
	     * pushobj
	     * newinit
	     * exception
	     * initprop <atom>                    marked SRC_CATCH
	     * enterwith
	     * [< catchguard code >]              if there's a catchguard
	     * ifeq <offset to next catch block>
	     * < catch block contents >
	     * leavewith
	     * goto <end of catch blocks>         non-local; finally applies
	     *
	     * If there's no catch block without a catchguard, the last
	     * <offset to next catch block> points to rethrow code.  This
	     * code will GOSUB to the finally code if appropriate, and is
	     * also used for the catch-all trynote for capturing exceptions
	     * thrown from catch{} blocks.
	     */
	    do {
		JSStmtInfo stmtInfo2;
		JSParseNode *disc;
		ptrdiff_t guardnote;

		iter = iter->pn_kid2;
		disc = iter->pn_kid1;

                if (!UpdateLinenoNotes(cx, cg, iter))
                    return JS_FALSE;

		if (catchjmp != -1) {
		    /* fix up and clean up previous catch block */
		    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
		    if ((uintN)++cg->stackDepth > cg->maxStackDepth)
			cg->maxStackDepth = cg->stackDepth;
		    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
			js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
			return JS_FALSE;
		} else {
		    /* set stack to original depth (see SETSP comment above) */
		    EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);
		}

		/* non-zero guardnote is length of catchguard */
		guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
		if (guardnote < 0 ||
		    js_Emit1(cx, cg, JSOP_NOP) < 0)
		    return JS_FALSE;

		/* construct the scope holder and push it on */
		ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,
				   &cg->atomList);
		if (!ale)
		    return JS_FALSE;
		EMIT_ATOM_INDEX_OP(JSOP_NAME, ale->index);

		if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 ||
		    js_Emit1(cx, cg, JSOP_NEWINIT) < 0 ||
		    js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
		    return JS_FALSE;

		/* setprop <atomIndex> */
		ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList);
		if (!ale)
		    return JS_FALSE;

		EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ale->index);
		if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		    js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
		    return JS_FALSE;

		/* boolean_expr */
		if (disc->pn_expr) {
		    ptrdiff_t guardstart = CG_OFFSET(cg);
		    if (!js_EmitTree(cx, cg, disc->pn_expr))
			return JS_FALSE;
		    /* ifeq <next block> */
		    catchjmp = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
		    if (catchjmp < 0)
			return JS_FALSE;
		    if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0,
					     (ptrdiff_t)CG_OFFSET(cg) -
					     guardstart))
			return JS_FALSE;
		}
		/* emit catch block */
		js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_WITH,
				 CG_OFFSET(cg));
		if (!js_EmitTree(cx, cg, iter->pn_kid3))
		    return JS_FALSE;
		js_PopStatementCG(cx, cg);

		/*
		 * jump over the remaining catch blocks
		 * this counts as a non-local jump, so do the finally thing
		 */

		/* popscope */
		if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		    js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
		    return JS_FALSE;

		/* gosub <finally>, if required */
		if (pn->pn_kid3)
		    EMIT_FINALLY_GOSUB(cx, cg);

		/* this will get fixed up to jump to after catch/finally */
		if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		    js_Emit3(cx, cg, JSOP_GOTO, 0, 0) < 0)
		    return JS_FALSE;
		if (!iter->pn_kid2)
		    break;
	    } while (iter);

	}

	/*
	 * we use a [leavewith],[gosub],rethrow block for rethrowing
	 * when there's no unguarded catch, and also for
	 * running finally code while letting an uncaught exception
	 * pass through
	 */
	if (pn->pn_kid3 ||
	    (catchjmp != -1 && iter->pn_kid1->pn_expr)) {
	    /*
	     * Emit another stack fix, because the catch could itself
	     * throw an exception in an unbalanced state, and the finally
	     * may need to call functions etc.
	     */
	    EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);

	    if (catchjmp != -1 && iter->pn_kid1->pn_expr) {
		CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
	    }
	    /* last discriminant jumps to rethrow if none match */
	    if ((uintN)++cg->stackDepth > cg->maxStackDepth)
		cg->maxStackDepth = cg->stackDepth;
	    if (pn->pn_kid2 &&
		(js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		 js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0))
		return JS_FALSE;

	    if (pn->pn_kid3) {
		finallyCatch = CG_OFFSET(cg);
		EMIT_FINALLY_GOSUB(cx, cg);
	    }
	    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 ||
		js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
		js_Emit1(cx, cg, JSOP_THROW) < 0)
		return JS_FALSE;
	}

	/*
	 * If we've got a finally, it goes here, and we have to fix up
	 * the gosubs that might have been emitted before non-local jumps.
	 */
	if (pn->pn_kid3) {
	    ptrdiff_t finallyIndex;
	    finallyIndex = CG_OFFSET(cg);
	    if (!FixupFinallyJumps(cx, cg, start, finallyIndex))
		return JS_FALSE;
	    js_PopStatementCG(cx, cg);
            if (!UpdateLinenoNotes(cx, cg, pn->pn_kid3))
                return JS_FALSE;
	    if (js_NewSrcNote2(cx, cg, SRC_TRYFIN, 1) < 0 ||
		js_Emit1(cx, cg, JSOP_NOP) < 0 ||
		!js_EmitTree(cx, cg, pn->pn_kid3) ||
		js_Emit1(cx, cg, JSOP_RETSUB) < 0)
		return JS_FALSE;
	}

	if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
	    js_Emit1(cx, cg, JSOP_NOP) < 0)
	    return JS_FALSE;

	/* fix up the end-of-try/catch jumps to come here */
	if (!FixupCatchJumps(cx, cg, start, CG_OFFSET(cg)))
	    return JS_FALSE;

	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);

	/*
	 * Add the try note last, to let post-order give us the right ordering
	 * (first to last, inner to outer).
	 */
	if (pn->pn_kid2 &&
	    !js_NewTryNote(cx, cg, start, end, catchStart))
	    return JS_FALSE;

	/*
	 * If we've got a finally, mark try+catch region with additional
	 * trynote to catch exceptions (re)thrown from a catch block or
	 * for the try{}finally{} case.
	 */
	if (pn->pn_kid3 &&
	    !js_NewTryNote(cx, cg, start, finallyCatch-1, finallyCatch))
	    return JS_FALSE;
	break;
      }

#endif /* JS_HAS_EXCEPTIONS */

      case TOK_VAR:
	off = noteIndex = -1;
	for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
	    JS_ASSERT(pn2->pn_type == TOK_NAME);
	    op = pn2->pn_op;
	    if (pn2->pn_slot >= 0) {
		atomIndex = (jsatomid) pn2->pn_slot;
	    } else {
		ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
		if (!ale)
		    return JS_FALSE;
		atomIndex = ale->index;
	    }
	    if (pn2->pn_expr) {
		if (op == JSOP_SETNAME2)
		    EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
		if (!js_EmitTree(cx, cg, pn2->pn_expr))
		    return JS_FALSE;
	    }
	    if (pn2 == pn->pn_head && js_NewSrcNote(cx, cg, SRC_VAR) < 0)
		return JS_FALSE;
	    EMIT_ATOM_INDEX_OP(op, atomIndex);
	    tmp = CG_OFFSET(cg);
	    if (noteIndex >= 0) {
		if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, tmp - off))
		    return JS_FALSE;
	    }
	    if (!pn2->pn_next)
		break;
	    off = tmp;
	    noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
	    if (noteIndex < 0 ||
		js_Emit1(cx, cg, JSOP_POP) < 0) {
		return JS_FALSE;
	    }
	}
	if (pn->pn_op != JSOP_NOP) {
	    if (js_Emit1(cx, cg, pn->pn_op) < 0)
		return JS_FALSE;
	}
	break;

      case TOK_RETURN:
	pn2 = pn->pn_kid;
	if (pn2) {
	    if (!js_EmitTree(cx, cg, pn2))
		return JS_FALSE;
	} else {
	    if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
		return JS_FALSE;
	}
	if (js_Emit1(cx, cg, JSOP_RETURN) < 0)
	    return JS_FALSE;
	break;

      case TOK_LC:
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
	for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
	    if (!js_EmitTree(cx, cg, pn2))
		return JS_FALSE;
	}
	return js_PopStatementCG(cx, cg);

      case TOK_SEMI:
	if (pn->pn_kid) {
	    if (!js_EmitTree(cx, cg, pn->pn_kid))
		return JS_FALSE;
	    if (js_Emit1(cx, cg, JSOP_POPV) < 0)
		return JS_FALSE;
	}
	break;

      case TOK_COLON:
	/* Emit an annotated nop so we know to decompile a label. */
	atom = pn->pn_atom;
	ale = js_IndexAtom(cx, atom, &cg->atomList);
	if (!ale)
	    return JS_FALSE;
	pn2 = pn->pn_expr;
	noteIndex = js_NewSrcNote2(cx, cg,
				   (pn2->pn_type == TOK_LC)
				   ? SRC_LABELBRACE
				   : SRC_LABEL,
				   (ptrdiff_t)ale->index);
	if (noteIndex < 0 ||
	    js_Emit1(cx, cg, JSOP_NOP) < 0) {
	    return JS_FALSE;
	}

	/* Emit code for the labeled statement. */
	js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, CG_OFFSET(cg));
	stmtInfo.label = atom;
	if (!js_EmitTree(cx, cg, pn2))
	    return JS_FALSE;
	if (!js_PopStatementCG(cx, cg))
	    return JS_FALSE;

	/* If the statement was compound, emit a note for the end brace. */
	if (pn2->pn_type == TOK_LC) {
	    if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
		js_Emit1(cx, cg, JSOP_NOP) < 0) {
		return JS_FALSE;
	    }
	}
	break;

      case TOK_COMMA:
	/*
	 * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
	 * These notes help the decompiler bracket the bytecodes generated
	 * from each sub-expression that follows a comma.
	 */
	off = noteIndex = -1;
	for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
	    if (!js_EmitTree(cx, cg, pn2))
		return JS_FALSE;
	    tmp = CG_OFFSET(cg);
	    if (noteIndex >= 0) {
		if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, tmp - off))
		    return JS_FALSE;
	    }
	    if (!pn2->pn_next)
		break;
	    off = tmp;
	    noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
	    if (noteIndex < 0 ||
		js_Emit1(cx, cg, JSOP_POP) < 0) {
		return JS_FALSE;
	    }
	}
	break;

      case TOK_ASSIGN:
	/*
	 * Check left operand type and generate specialized code for it.
	 * Specialize to avoid ECMA "reference type" values on the operand
	 * stack, which impose pervasive runtime "GetValue" costs.
	 */
	pn2 = pn->pn_left;
	JS_ASSERT(pn2->pn_type != TOK_RP);
	atomIndex = -1;
	switch (pn2->pn_type) {
	  case TOK_NAME:
	    if (pn2->pn_slot >= 0) {
		atomIndex = (jsatomid) pn2->pn_slot;
	    } else {
		ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
		if (!ale)
		    return JS_FALSE;
		atomIndex = ale->index;
		EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
	    }
	    break;
	  case TOK_DOT:
	    if (!js_EmitTree(cx, cg, pn2->pn_expr))
		return JS_FALSE;
	    ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
	    if (!ale)
		return JS_FALSE;
	    atomIndex = ale->index;
	    break;
	  case TOK_LB:
	    if (!js_EmitTree(cx, cg, pn2->pn_left))
		return JS_FALSE;
	    if (!js_EmitTree(cx, cg, pn2->pn_right))
		return JS_FALSE;
	    break;
	  default:
	    JS_ASSERT(0);
	}

	/* If += or similar, dup the left operand and get its value. */
	op = pn->pn_op;
	if (op != JSOP_NOP) {
	    switch (pn2->pn_type) {
	      case TOK_NAME:
		if (pn2->pn_op != JSOP_SETNAME2) {
		    EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETARG)
				       ? JSOP_GETARG
				       : JSOP_GETVAR,
				       atomIndex);
		    break;
		}
		/* FALL THROUGH */
	      case TOK_DOT:
		if (js_Emit1(cx, cg, JSOP_DUP) < 0)
		    return JS_FALSE;
		EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex);
		break;
	      case TOK_LB:
		if (js_Emit1(cx, cg, JSOP_DUP2) < 0)
		    return JS_FALSE;
		if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
		    return JS_FALSE;
		break;
	      default:;
	    }
	}

	/* Now emit the right operand (it may affect the namespace). */
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;

	/* If += etc., emit the binary operator with a decompiler note. */
	if (op != JSOP_NOP) {
	    if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 ||
		js_Emit1(cx, cg, op) < 0) {
		return JS_FALSE;
	    }
	}

	/* Left parts such as a.b.c and a[b].c need a decompiler note. */
	if (pn2->pn_type != TOK_NAME) {
	    if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
			       (ptrdiff_t)(CG_OFFSET(cg) - top)) < 0) {
		return JS_FALSE;
	    }
	}

	/* Finally, emit the specialized assignment bytecode. */
	switch (pn2->pn_type) {
	  case TOK_NAME:
	  case TOK_DOT:
	    EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
	    break;
	  case TOK_LB:
	    if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
		return JS_FALSE;
	    break;
	  default:;
	}
	break;

      case TOK_HOOK:
	/* Emit the condition, then branch if false to the else part. */
	if (!js_EmitTree(cx, cg, pn->pn_kid1))
	    return JS_FALSE;
	if (js_NewSrcNote(cx, cg, SRC_COND) < 0)
	    return JS_FALSE;
	beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
	if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2))
	    return JS_FALSE;

	/* Jump around else, fixup the branch, emit else, fixup jump. */
	jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	if (jmp < 0)
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
	if (!js_EmitTree(cx, cg, pn->pn_kid3))
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
	break;

      case TOK_OR:
	/* Emit left operand, emit pop-if-converts-to-false-else-jump. */
	if (!js_EmitTree(cx, cg, pn->pn_left))
	    return JS_FALSE;
#if JS_BUG_SHORT_CIRCUIT
	beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);
	tmp = js_Emit1(cx, cg, JSOP_TRUE);
	jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	if (beq < 0 || tmp < 0 || jmp < 0)
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
#else
	/*
	 * JSOP_OR converts the operand on the stack to boolean, and if true,
	 * leaves the original operand value on the stack and jumps; otherwise
	 * it pops and falls into the next bytecode.
	 */
	jmp = js_Emit3(cx, cg, JSOP_OR, 0, 0);
	if (jmp < 0)
	    return JS_FALSE;
#endif
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
	break;

      case TOK_AND:
	/* && is like || except it uses a pop-if-converts-to-true-else-jump. */
	if (!js_EmitTree(cx, cg, pn->pn_left))
	    return JS_FALSE;
#if JS_BUG_SHORT_CIRCUIT
	beq = js_Emit3(cx, cg, JSOP_IFNE, 0, 0);
	tmp = js_Emit1(cx, cg, JSOP_FALSE);
	jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);
	if (beq < 0 || tmp < 0 || jmp < 0)
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
#else
	jmp = js_Emit3(cx, cg, JSOP_AND, 0, 0);
	if (jmp < 0)
	    return JS_FALSE;
#endif
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;
	CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
	break;

      case TOK_BITOR:
      case TOK_BITXOR:
      case TOK_BITAND:
      case TOK_EQOP:
      case TOK_RELOP:
#if JS_HAS_IN_OPERATOR
      case TOK_IN:
#endif
#if JS_HAS_INSTANCEOF
      case TOK_INSTANCEOF:
#endif
      case TOK_SHOP:
      case TOK_PLUS:
      case TOK_MINUS:
      case TOK_STAR:
      case TOK_DIVOP:
	/* Binary operators that evaluate both operands unconditionally. */
	if (!js_EmitTree(cx, cg, pn->pn_left))
	    return JS_FALSE;
	if (!js_EmitTree(cx, cg, pn->pn_right))
	    return JS_FALSE;
	if (js_Emit1(cx, cg, pn->pn_op) < 0)
	    return JS_FALSE;
	break;

#if JS_HAS_EXCEPTIONS
      case TOK_THROW:
#endif
      case TOK_UNARYOP:
	/* Unary op, including unary +/-. */
	if (!js_EmitTree(cx, cg, pn->pn_kid))
	    return JS_FALSE;
	if (js_Emit1(cx, cg, pn->pn_op) < 0)
	    return JS_FALSE;
	break;

      case TOK_INC:
      case TOK_DEC:
	/* Emit lvalue-specialized code for ++/-- operators. */
	pn2 = pn->pn_kid;
	JS_ASSERT(pn2->pn_type != TOK_RP);
	op = pn->pn_op;
	switch (pn2->pn_type) {
	  case TOK_NAME:
	    if (pn->pn_num >= 0) {
		atomIndex = (jsatomid) pn->pn_num;
		EMIT_ATOM_INDEX_OP(op, atomIndex);
	    } else {
		if (!EmitAtomOp(cx, pn2, op, cg))
		    return JS_FALSE;
	    }
	    break;
	  case TOK_DOT:
	    if (!EmitPropOp(cx, pn2, op, cg))
		return JS_FALSE;
	    break;
	  case TOK_LB:
	    if (!EmitElemOp(cx, pn2, op, cg))
		return JS_FALSE;
	    break;
	  default:
	    JS_ASSERT(0);
	}
	break;

      case TOK_NEW:
	/* Code for (new f()) and f() are the same, except for the opcode. */
	op = JSOP_NEW;
	goto emit_call;

      case TOK_DELETE:
	/* Delete is also lvalue-specialized to avoid reference types. */
	pn2 = pn->pn_kid;
	JS_ASSERT(pn2->pn_type != TOK_RP);
	switch (pn2->pn_type) {
	  case TOK_NAME:
	    if (!EmitAtomOp(cx, pn2, JSOP_DELNAME, cg))
		return JS_FALSE;
	    break;
	  case TOK_DOT:
	    if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))
		return JS_FALSE;
	    break;
	  case TOK_LB:
	    if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))
		return JS_FALSE;
	    break;
	  default:
	    JS_ASSERT(0);
	}
	break;

      case TOK_DOT:
	/*
	 * Pop a stack operand, convert it to object, get a property named by
	 * this bytecode's immediate-indexed atom operand, and push its value
	 * (not a reference to it).  This bytecode sets the virtual machine's
	 * "obj" register to the left operand's ToObject conversion result,
	 * for use by JSOP_PUSHOBJ.
	 */
	return EmitPropOp(cx, pn, pn->pn_op, cg);

      case TOK_LB:
	/*
	 * Pop two operands, convert the left one to object and the right one
	 * to property name (atom or tagged int), get the named property, and
	 * push its value.  Set the "obj" register to the result of ToObject
	 * on the left operand.
	 */
	return EmitElemOp(cx, pn, pn->pn_op, cg);

      case TOK_LP:
	/*
	 * Emit function call or operator new (constructor call) code.  First
	 * emit code for the left operand to evaluate the call- or construct-
	 * able object expression.
	 */
	op = JSOP_CALL;
      emit_call:
	pn2 = pn->pn_head;
	if ((cx->version == JSVERSION_DEFAULT || cx->version >= JSVERSION_1_4)
                && (pn2->pn_op == JSOP_NAME)
                /*
                * below, is it sufficient to compare the atom values ?
                */
                 && (ATOM_KEY(pn2->pn_atom)
                            == ATOM_KEY(cx->runtime->atomState.evalAtom)))
            op = JSOP_CALLSPECIAL;

	if (!js_EmitTree(cx, cg, pn2))
	    return JS_FALSE;

	/*
	 * Push the virtual machine's "obj" register, which was set by a name,
	 * property, or element get (or set) bytecode.
	 */
	if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
	    return JS_FALSE;

	/*
	 * Emit code for each argument in order, then emit the JSOP_CALL or
	 * JSOP_NEW bytecode with a two-byte immediate telling how many args
	 * were pushed on the operand stack.
	 */
	for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
	    if (!js_EmitTree(cx, cg, pn2))
		return JS_FALSE;
	}
	argc = pn->pn_count - 1;
	if (js_Emit3(cx, cg, op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
	    return JS_FALSE;
	break;

#if JS_HAS_INITIALIZERS
      case TOK_RB:
	/*
	 * Emit code for [a, b, c] of the form:
	 *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
	 * but use a stack slot for t and avoid dup'ing and popping it via
	 * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
	 */
	ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom,
			   &cg->atomList);
	if (!ale)
	    return JS_FALSE;
	EMIT_ATOM_INDEX_OP(JSOP_NAME, ale->index);
	if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
	    return JS_FALSE;
	if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
	    return JS_FALSE;

	pn2 = pn->pn_head;
#if JS_HAS_SHARP_VARS
	if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
	    EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
	    pn2 = pn2->pn_next;
	}
#endif

	for (atomIndex = 0; pn2; pn2 = pn2->pn_next) {
	    /* PrimaryExpr enforced ATOM_INDEX_LIMIT, so in-line optimize. */
	    JS_ASSERT(atomIndex < ATOM_INDEX_LIMIT);
	    if (atomIndex == 0) {
		if (js_Emit1(cx, cg, JSOP_ZERO) < 0)
		    return JS_FALSE;
	    } else if (atomIndex == 1) {
		if (js_Emit1(cx, cg, JSOP_ONE) < 0)
		    return JS_FALSE;
	    } else {
		EMIT_ATOM_INDEX_OP(JSOP_UINT16, (jsatomid)atomIndex);
	    }

	    /* Sub-optimal: holes in a sparse initializer are void-filled. */
	    if (pn2->pn_type == TOK_COMMA) {
		if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
		    return JS_FALSE;
	    } else {
		if (!js_EmitTree(cx, cg, pn2))
		    return JS_FALSE;
	    }
	    if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
		return JS_FALSE;

	    atomIndex++;
	}

	if (pn->pn_extra) {
	    /* Emit a source note so we know to decompile an extra comma. */
	    if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
		return JS_FALSE;
	}

	/* Emit an op for sharp array cleanup and decompilation. */
	if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
	    return JS_FALSE;
	break;

      case TOK_RC:
	/*
	 * Emit code for {p:a, '%q':b, 2:c} of the form:
	 *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;
	 * but use a stack slot for t and avoid dup'ing and popping it via
	 * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
	 */
	ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,
			   &cg->atomList);
	if (!ale)
	    return JS_FALSE;
	EMIT_ATOM_INDEX_OP(JSOP_NAME, ale->index);

	if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
	    return JS_FALSE;
	if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
	    return JS_FALSE;

	pn2 = pn->pn_head;
#if JS_HAS_SHARP_VARS
	if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
	    EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
	    pn2 = pn2->pn_next;
	}
#endif

	for (; pn2; pn2 = pn2->pn_next) {
	    /* Emit an index for t[2], else map an atom for t.p or t['%q']. */
	    pn3 = pn2->pn_left;
	    switch (pn3->pn_type) {
	      case TOK_NUMBER:
		if (!EmitNumberOp(cx, pn3->pn_dval, cg))
		    return JS_FALSE;
		break;
	      case TOK_NAME:
	      case TOK_STRING:
		ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
		if (!ale)
		    return JS_FALSE;
		break;
	      default:
		JS_ASSERT(0);
	    }

	    /* Emit code for the property initializer. */
	    if (!js_EmitTree(cx, cg, pn2->pn_right))
		return JS_FALSE;

	    /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
	    if (pn3->pn_type == TOK_NUMBER) {
		if (js_NewSrcNote(cx, cg, SRC_LABEL) < 0)
		    return JS_FALSE;
		if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
		    return JS_FALSE;
	    } else {
		EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ale->index);
	    }
	}

	/* Emit an op for sharpArray cleanup and decompilation. */
	if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
	    return JS_FALSE;
	break;

#if JS_HAS_SHARP_VARS
      case TOK_DEFSHARP:
	if (!js_EmitTree(cx, cg, pn->pn_kid))
	    return JS_FALSE;
	EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);
	break;

      case TOK_USESHARP:
	EMIT_ATOM_INDEX_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
	break;
#endif /* JS_HAS_SHARP_VARS */
#endif /* JS_HAS_INITIALIZERS */

      case TOK_RP:
	/*
	 * The node for (e) has e as its kid, enabling users who want to nest
	 * assignment expressions in conditions to avoid the error correction
	 * done by Condition (from x = y to x == y) by double-parenthesizing.
	 *
	 * We also emit an annotated NOP so we can decompile user parentheses,
	 * but that's just a nicety (the decompiler does not preserve comments
	 * or white space, and it parenthesizes for correct precedence anyway,
	 * so this nop nicety should be considered with a cold eye, especially
	 * if another srcnote type is needed).
	 */
	if (!js_EmitTree(cx, cg, pn->pn_kid))
	    return JS_FALSE;

	if (js_NewSrcNote(cx, cg, SRC_PAREN) < 0 ||
	    js_Emit1(cx, cg, JSOP_NOP) < 0) {
	    return JS_FALSE;
	}
	break;

      case TOK_NAME:
	if (pn->pn_slot >= 0) {
	    EMIT_ATOM_INDEX_OP(pn->pn_op, (jsatomid) pn->pn_slot);
	    break;
	}
	/* FALL THROUGH */
      case TOK_STRING:
      case TOK_OBJECT:
	/*
	 * The scanner and parser associate JSOP_NAME with TOK_NAME, although
	 * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME2,
	 * JSOP_FORNAME2, etc.).  Among JSOP_*NAME* variants, only JSOP_NAME
	 * may generate the first operand of a call or new expression, so only
	 * it sets the "obj" virtual machine register to the object along the
	 * scope chain in which the name was found.
	 *
	 * Token types for STRING and OBJECT have corresponding bytecode ops
	 * in pn_op and emit the same format as NAME, so they share this code.
	 */
	return EmitAtomOp(cx, pn, pn->pn_op, cg);

      case TOK_NUMBER:
	return EmitNumberOp(cx, pn->pn_dval, cg);

      case TOK_PRIMARY:
	return js_Emit1(cx, cg, pn->pn_op) >= 0;

#if JS_HAS_DEBUGGER_KEYWORD
      case TOK_DEBUGGER:
	return js_Emit1(cx, cg, JSOP_DEBUGGER) >= 0;
#endif /* JS_HAS_DEBUGGER_KEYWORD */

      default:
	JS_ASSERT(0);
    }

    return JS_TRUE;
}

JS_FRIEND_DATA(const char *) js_SrcNoteName[] = {
    "null",
    "if",
    "if-else",
    "while",
    "for",
    "continue",
    "var",
    "pcdelta",
    "assignop",
    "cond",
    "paren",
    "hidden",
    "pcbase",
    "label",
    "labelbrace",
    "endbrace",
    "break2label",
    "cont2label",
    "switch",
    "funcdef",
    "tryfin",
    "catch",
    "newline",
    "setline",
    "xdelta"
};

uint8 js_SrcNoteArity[] = {
    0,  /* SRC_NULL */
    0,  /* SRC_IF */
    0,  /* SRC_IF_ELSE */
    0,  /* SRC_WHILE */
    3,  /* SRC_FOR */
    0,  /* SRC_CONTINUE */
    0,  /* SRC_VAR */
    1,  /* SRC_PCDELTA */
    0,  /* SRC_ASSIGNOP */
    0,  /* SRC_COND */
    0,  /* SRC_PAREN */
    0,  /* SRC_HIDDEN */
    1,  /* SRC_PCBASE */
    1,  /* SRC_LABEL */
    1,  /* SRC_LABELBRACE */
    0,  /* SRC_ENDBRACE */
    1,  /* SRC_BREAK2LABEL */
    1,  /* SRC_CONT2LABEL */
    2,  /* SRC_SWITCH */
    1,  /* SRC_FUNCDEF */
    1,  /* SRC_TRYFIN */
    1,  /* SRC_CATCH */
    0,  /* SRC_NEWLINE */
    1,  /* SRC_SETLINE */
    0   /* SRC_XDELTA */
};

static intN
AllocSrcNote(JSContext *cx, JSCodeGenerator *cg)
{
    intN index;
    JSArenaPool *pool;
    size_t incr, size;

    index = cg->noteCount;
    if (index % SNINCR == 0) {
	pool = &cx->tempPool;
	incr = SNINCR * sizeof(jssrcnote);
	if (!cg->notes) {
	    JS_ARENA_ALLOCATE(cg->notes, pool, incr);
	} else {
	    size = cg->noteCount * sizeof(jssrcnote);
	    JS_ARENA_GROW(cg->notes, pool, size, incr);
	}
	if (!cg->notes) {
	    JS_ReportOutOfMemory(cx);
	    return -1;
	}
    }

    cg->noteCount = index + 1;
    return index;
}

intN
js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
{
    intN index, n;
    jssrcnote *sn;
    ptrdiff_t offset, delta, xdelta;

    /*
     * Claim a note slot in cg->notes by growing it if necessary and then
     * incrementing cg->noteCount.
     */
    index = AllocSrcNote(cx, cg);
    sn = &cg->notes[index];

    /*
     * Compute delta from the last annotated bytecode's offset.  If it's too
     * big to fit in sn, allocate one or more xdelta notes and reset sn.
     */
    offset = CG_OFFSET(cg);
    delta = offset - cg->lastNoteOffset;
    cg->lastNoteOffset = offset;
    if (delta >= SN_DELTA_LIMIT) {
	do {
	    xdelta = JS_MIN(delta, SN_XDELTA_MASK);
	    SN_MAKE_XDELTA(sn, xdelta);
	    delta -= xdelta;
	    index = js_NewSrcNote(cx, cg, SRC_NULL);
	    if (index < 0)
		return -1;
	    sn = &cg->notes[index];
	} while (delta >= SN_DELTA_LIMIT);
    }

    /*
     * Initialize type and delta, then allocate the minimum number of notes
     * needed for type's arity.  Usually, we won't need more, but if an offset
     * does take two bytes, js_SetSrcNoteOffset will grow cg->notes.
     */
    SN_MAKE_NOTE(sn, type, delta);
    for (n = (intN)js_SrcNoteArity[type]; n > 0; n--) {
	if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
	    return -1;
    }
    return index;
}

intN
js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
	       ptrdiff_t offset)
{
    intN index;

    index = js_NewSrcNote(cx, cg, type);
    if (index >= 0) {
	if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
	    return -1;
    }
    return index;
}

intN
js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
	       ptrdiff_t offset1, ptrdiff_t offset2)
{
    intN index;

    index = js_NewSrcNote(cx, cg, type);
    if (index >= 0) {
	if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1))
	    return -1;
	if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2))
	    return -1;
    }
    return index;
}

static JSBool
GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg)
{
    JSArenaPool *pool;
    size_t incr, size;

    pool = &cx->tempPool;
    incr = SNINCR * sizeof(jssrcnote);
    size = cg->noteCount * sizeof(jssrcnote);
    JS_ARENA_GROW(cg->notes, pool, size, incr);
    if (!cg->notes) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
    return JS_TRUE;
}

uintN
js_SrcNoteLength(jssrcnote *sn)
{
    intN arity;
    jssrcnote *base;

    arity = (intN)js_SrcNoteArity[SN_TYPE(sn)];
    if (!arity)
	return 1;
    for (base = sn++; --arity >= 0; sn++) {
	if (*sn & SN_3BYTE_OFFSET_FLAG)
	    sn +=2;
    }
    return sn - base;
}

JS_FRIEND_API(ptrdiff_t)
js_GetSrcNoteOffset(jssrcnote *sn, uintN which)
{
    /* Find the offset numbered which (i.e., skip exactly which offsets). */
    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
    JS_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]);
    for (sn++; which; sn++, which--) {
	if (*sn & SN_3BYTE_OFFSET_FLAG)
	    sn += 2;
    }
    if (*sn & SN_3BYTE_OFFSET_FLAG) {
	return (ptrdiff_t)((((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK)) << 16)
			   | (sn[1] << 8) | sn[2]);
    }
    return (ptrdiff_t)*sn;
}

JSBool
js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
		    uintN which, ptrdiff_t offset)
{
    jssrcnote *sn;
    ptrdiff_t diff;

    if (offset >= (((ptrdiff_t)SN_3BYTE_OFFSET_FLAG) << 16)) {
	ReportStatementTooLarge(cx, cg);
	return JS_FALSE;
    }

    /* Find the offset numbered which (i.e., skip exactly which offsets). */
    sn = &cg->notes[index];
    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
    JS_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]);
    for (sn++; which; sn++, which--) {
	if (*sn & SN_3BYTE_OFFSET_FLAG)
	    sn += 2;
    }

    /* See if the new offset requires three bytes. */
    if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
	/* Maybe this offset was already set to a three-byte value. */
	if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
	    /* Losing, need to insert another two bytes for this offset. */
	    index = sn - cg->notes;
	    cg->noteCount += 2;

	    /*
	     * Simultaneously test to see if the source note array must grow to
	     * accomodate either the first or second byte of additional storage
	     * required by this 3-byte offset.
	     */
	    if ((cg->noteCount - 1) % SNINCR <= 1) {
		if (!GrowSrcNotes(cx, cg))
		    return JS_FALSE;
		sn = cg->notes + index;
	    }
	    diff = cg->noteCount - (index + 3);
	    JS_ASSERT(diff >= 0);
	    if (diff > 0)
		memmove(sn + 3, sn + 1, diff * sizeof(jssrcnote));
	}
	*sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
	*sn++ = (jssrcnote)(offset >> 8);
    }
    *sn = (jssrcnote)offset;
    return JS_TRUE;
}

jssrcnote *
js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg)
{
    uintN count;
    jssrcnote *tmp, *final;

    count = cg->noteCount;
    tmp   = cg->notes;
    final = JS_malloc(cx, (count + 1) * sizeof(jssrcnote));
    if (!final)
	return NULL;
    memcpy(final, tmp, count * sizeof(jssrcnote));
    SN_MAKE_TERMINATOR(&final[count]);
    return final;
}

JSBool
js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
{
    size_t size, incr;
    ptrdiff_t delta;

    size = cg->treeContext.tryCount * sizeof(JSTryNote);
    if (size <= cg->tryNoteSpace)
	return JS_TRUE;

    if (!cg->tryBase) {
	size = JS_ROUNDUP(size, TNINCR);
	JS_ARENA_ALLOCATE(cg->tryBase, &cx->tempPool, size);
	if (!cg->tryBase)
	    return JS_FALSE;
	cg->tryNoteSpace = size;
	cg->tryNext = cg->tryBase;
    } else {
	delta = (char *)cg->tryNext - (char *)cg->tryBase;
	incr = size - cg->tryNoteSpace;
	incr = JS_ROUNDUP(incr, TNINCR);
	size = cg->tryNoteSpace;
	JS_ARENA_GROW(cg->tryBase, &cx->tempPool, size, incr);
	if (!cg->tryBase)
	    return JS_FALSE;
	cg->tryNoteSpace = size + incr;
	cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta);
    }
    return JS_TRUE;
}

JSTryNote *
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
	      ptrdiff_t end, ptrdiff_t catchStart)
{
    JSTryNote *tn;

    JS_ASSERT(cg->tryBase <= cg->tryNext);
    JS_ASSERT(catchStart >= 0);
    tn = cg->tryNext++;
    tn->start = start;
    tn->length = end - start;
    tn->catchStart = catchStart;
    return tn;
}

JSBool
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp)
{
    uintN count;
    JSTryNote *tmp, *final;

    count = cg->tryNext - cg->tryBase;
    if (!count) {
	*tryp = NULL;
	return JS_TRUE;
    }

    tmp = cg->tryBase;
    final = JS_malloc(cx, (count + 1) * sizeof(JSTryNote));
    if (!final) {
	*tryp = NULL;
	return JS_FALSE;
    }
    memcpy(final, tmp, count * sizeof(JSTryNote));
    final[count].start = 0;
    final[count].length = CG_OFFSET(cg);
    final[count].catchStart = 0;
    *tryp = final;
    return JS_TRUE;
}

**** End of jsemit.c. ****

**** Start of jsemit.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsemit_h___
#define jsemit_h___
/*
 * JS bytecode generation.
 */

#include "jsstddef.h"
#include "jstypes.h"
#include "jsatom.h"
#include "jsopcode.h"
#include "jsprvtd.h"
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

typedef enum JSStmtType {
    STMT_BLOCK        = 0,      /* compound statement: { s1[;... sN] } */
    STMT_LABEL        = 1,      /* labeled statement:  l: s */
    STMT_IF           = 2,      /* if (then) statement */
    STMT_ELSE         = 3,      /* else statement */
    STMT_SWITCH       = 4,      /* switch statement */
    STMT_WITH         = 5,      /* with statement */
    STMT_TRY	      = 6,	/* try statement */
    STMT_CATCH	      = 7,	/* catch block */
    STMT_FINALLY      = 8,	/* finally statement */
    STMT_DO_LOOP      = 9,      /* do/while loop statement */
    STMT_FOR_LOOP     = 10,     /* for loop statement */
    STMT_FOR_IN_LOOP  = 11,     /* for/in loop statement */
    STMT_WHILE_LOOP   = 12      /* while loop statement */
} JSStmtType;

#define STMT_IS_LOOP(stmt)      ((stmt)->type >= STMT_DO_LOOP)

typedef struct JSStmtInfo JSStmtInfo;

struct JSStmtInfo {
    JSStmtType      type;           /* statement type */
    ptrdiff_t       top;            /* offset of loop top from cg base */
    ptrdiff_t       update;         /* loop update offset (top if none) */
    ptrdiff_t       breaks;         /* offset of last break in loop */
    ptrdiff_t       continues;      /* offset of last continue in loop */
    JSAtom          *label;         /* label name if type is STMT_LABEL */
    JSStmtInfo      *down;          /* info for enclosing statement */
};

#define SET_STATEMENT_TOP(stmt, top) \
    ((stmt)->top = (stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))

struct JSTreeContext {              /* tree context for semantic checks */
    uint32          flags;          /* statement state flags, see below */
    uint32          tryCount;       /* total count of try statements parsed */
    JSStmtInfo      *topStmt;       /* top of statement info stack */
};

#define TCF_IN_FUNCTION 0x01        /* parsing inside function body */
#define TCF_RETURN_EXPR 0x02        /* function has 'return expr;' */
#define TCF_RETURN_VOID 0x04        /* function has 'return;' */
#define TCF_IN_FOR_INIT 0x08        /* parsing init expr of for; exclude 'in' */

#define TREE_CONTEXT_INIT(tc) \
    ((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL)

struct JSCodeGenerator {
    void            *codeMark;      /* low watermark in cx->codePool */
    void            *tempMark;      /* low watermark in cx->tempPool */
    jsbytecode      *base;          /* base of JS bytecode vector */
    jsbytecode      *limit;         /* one byte beyond end of bytecode */
    jsbytecode      *next;          /* pointer to next free bytecode */
    const char      *filename;      /* null or weak link to source filename */
    uintN           firstLine;      /* first line, for js_NewScriptFromCG */
    uintN           currentLine;    /* line number for tree-based srcnote gen */
    JSPrincipals    *principals;    /* principals for constant folding eval */
    JSTreeContext   treeContext;    /* for break/continue code generation */
    JSAtomList      atomList;       /* literals indexed for mapping */
    intN            stackDepth;     /* current stack depth in basic block */
    uintN           maxStackDepth;  /* maximum stack depth so far */
    jssrcnote       *notes;         /* source notes, see below */
    uintN           noteCount;      /* number of source notes so far */
    ptrdiff_t       lastNoteOffset; /* code offset for last source note */
    JSTryNote       *tryBase;       /* first exception handling note */
    JSTryNote       *tryNext;       /* next available note */
    size_t          tryNoteSpace;   /* # of bytes allocated at tryBase */
};

#define CG_CODE(cg,offset)  ((cg)->base + (offset))
#define CG_OFFSET(cg)       PTRDIFF((cg)->next, (cg)->base, jsbytecode)

/*
 * Initialize cg to allocate bytecode space from cx->codePool, and srcnote
 * space from cx->tempPool.  Return true on success.  Report an error and
 * return false if the initial code segment can't be allocated.
 */
extern JS_FRIEND_API(JSBool)
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
		     const char *filename, uintN lineno,
		     JSPrincipals *principals);

/*
 * Release cx->codePool and cx->tempPool to marks set by js_InitCodeGenerator.
 * Note that cgs are magic: they own tempPool and codePool "tops-of-stack" 
 * above their codeMark and tempMark points.  This means you cannot alloc
 * from tempPool and save the pointer beyond the next JS_FinishCodeGenerator.
 */
extern JS_FRIEND_API(void)
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg);

/*
 * Emit one bytecode.
 */
extern ptrdiff_t
js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op);

/*
 * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1).
 */
extern ptrdiff_t
js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1);

/*
 * Emit three bytecodes, an opcode with two bytes of immediate operands.
 */
extern ptrdiff_t
js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,
	 jsbytecode op2);

/*
 * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
 */
extern ptrdiff_t
js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra);

/*
 * Unsafe macro to call js_SetJumpOffset and return false if it does.
 */
#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off)                               \
    JS_BEGIN_MACRO                                                            \
	if (!js_SetJumpOffset(cx, cg, pc, off))                               \
	    return JS_FALSE;                                                  \
    JS_END_MACRO

#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off)                               \
    CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off))

extern JSBool
js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
		 ptrdiff_t off);

/*
 * Push the C-stack-allocated struct at stmt onto the stmtInfo stack.
 */
extern void
js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
		 ptrdiff_t top);

/*
 * Emit a break instruction, recording it for backpatching.
 */
extern ptrdiff_t
js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
	     JSAtomListElement *label);

/*
 * Emit a continue instruction, recording it for backpatching.
 */
extern ptrdiff_t
js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
		JSAtomListElement *label);

/*
 * Pop tc->topStmt.  If the top JSStmtInfo struct is not stack-allocated, it
 * is up to the caller to free it.
 */
extern void
js_PopStatement(JSTreeContext *tc);

/*
 * Like js_PopStatement(&cg->treeContext), also patch breaks and continues.
 * May fail if a jump offset overflows.
 */
extern JSBool
js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg);

/*
 * Emit code into cg for the tree rooted at pn.
 */
extern JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn);

/*
 * Emit code into cg for the tree rooted at body, then create a persistent
 * script for fun from cg.
 */
extern JSBool
js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
		    JSFunction *fun);

/*
 * Source notes generated along with bytecode for decompiling and debugging.
 * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of
 * the previous note.  If 3 bits of offset aren't enough, extended delta notes
 * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits
 * are emitted before the next note.  Some notes have operand offsets encoded
 * immediately after them, in note bytes or byte-triples.
 *
 * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE,
 * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.
 *
 * NB: the js_SrcNoteName and js_SrcNoteArity arrays in jsemit.c are indexed
 * by this enum, so their initializers need to match the order here.
 */
typedef enum JSSrcNoteType {
    SRC_NULL        = 0,        /* terminates a note vector */
    SRC_IF          = 1,        /* JSOP_IFEQ bytecode is from an if-then */
    SRC_IF_ELSE     = 2,        /* JSOP_IFEQ bytecode is from an if-then-else */
    SRC_WHILE       = 3,        /* JSOP_IFEQ is from a while loop */
    SRC_FOR         = 4,        /* JSOP_NOP or JSOP_POP in for loop head */
    SRC_CONTINUE    = 5,        /* JSOP_GOTO is a continue, not a break;
                                   also used on JSOP_ENDINIT if extra comma
                                   at end of array literal: [1,2,,] */
    SRC_VAR         = 6,        /* JSOP_NAME/FORNAME with a var declaration */
    SRC_PCDELTA     = 7,        /* offset from comma-operator to next POP,
				   or from CONDSWITCH to first CASE opcode */
    SRC_ASSIGNOP    = 8,        /* += or another assign-op follows */
    SRC_COND        = 9,        /* JSOP_IFEQ is from conditional ?: operator */
    SRC_PAREN       = 10,       /* JSOP_NOP generated to mark user parens */
    SRC_HIDDEN      = 11,       /* opcode shouldn't be decompiled */
    SRC_PCBASE      = 12,       /* offset of first obj.prop.subprop bytecode */
    SRC_LABEL       = 13,       /* JSOP_NOP for label: with atomid immediate */
    SRC_LABELBRACE  = 14,       /* JSOP_NOP for label: {...} begin brace */
    SRC_ENDBRACE    = 15,       /* JSOP_NOP for label: {...} end brace */
    SRC_BREAK2LABEL = 16,       /* JSOP_GOTO for 'break label' with atomid */
    SRC_CONT2LABEL  = 17,       /* JSOP_GOTO for 'continue label' with atomid */
    SRC_SWITCH      = 18,       /* JSOP_*SWITCH with offset to end of switch,
				   2nd off to first JSOP_CASE if condswitch */
    SRC_FUNCDEF     = 19,       /* JSOP_NOP for function f() with atomid */
    SRC_TRYFIN	    = 20,       /* JSOP_NOP for try or finally section */
    SRC_CATCH       = 21,       /* catch block has guard */
    SRC_NEWLINE     = 22,       /* bytecode follows a source newline */
    SRC_SETLINE     = 23,       /* a file-absolute source line number note */
    SRC_XDELTA      = 24        /* 24-31 are for extended delta notes */
} JSSrcNoteType;

#define SN_TYPE_BITS            5
#define SN_DELTA_BITS           3
#define SN_XDELTA_BITS          6
#define SN_TYPE_MASK            (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS)
#define SN_DELTA_MASK           ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS))
#define SN_XDELTA_MASK          ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS))

#define SN_MAKE_NOTE(sn,t,d)    (*(sn) = (jssrcnote)                          \
					  (((t) << SN_DELTA_BITS)             \
					   | ((d) & SN_DELTA_MASK)))
#define SN_MAKE_XDELTA(sn,d)    (*(sn) = (jssrcnote)                          \
					  ((SRC_XDELTA << SN_DELTA_BITS)      \
					   | ((d) & SN_XDELTA_MASK)))

#define SN_IS_XDELTA(sn)        ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA)
#define SN_TYPE(sn)             (SN_IS_XDELTA(sn) ? SRC_XDELTA                \
						  : *(sn) >> SN_DELTA_BITS)
#define SN_SET_TYPE(sn,type)    SN_MAKE_NOTE(sn, type, SN_DELTA(sn))
#define SN_IS_GETTABLE(sn)      (SN_TYPE(sn) < SRC_NEWLINE)

#define SN_DELTA(sn)            ((ptrdiff_t)(SN_IS_XDELTA(sn)                 \
					     ? *(sn) & SN_XDELTA_MASK         \
					     : *(sn) & SN_DELTA_MASK))
#define SN_SET_DELTA(sn,delta)  (SN_IS_XDELTA(sn)                             \
				 ? SN_MAKE_XDELTA(sn, delta)                  \
				 : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta))

#define SN_DELTA_LIMIT          ((ptrdiff_t)JS_BIT(SN_DELTA_BITS))
#define SN_XDELTA_LIMIT         ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS))

/*
 * Offset fields follow certain notes and are frequency-encoded: an offset in
 * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and
 * the high bit of the first byte is set.
 */
#define SN_3BYTE_OFFSET_FLAG    0x80
#define SN_3BYTE_OFFSET_MASK    0x7f

extern JS_FRIEND_DATA(const char *) js_SrcNoteName[];
extern JS_FRIEND_DATA(uint8) js_SrcNoteArity[];
extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn);

#define SN_LENGTH(sn)           ((js_SrcNoteArity[SN_TYPE(sn)] == 0) ? 1      \
				 : js_SrcNoteLength(sn))
#define SN_NEXT(sn)             ((sn) + SN_LENGTH(sn))

/* A source note array is terminated by an all-zero element. */
#define SN_MAKE_TERMINATOR(sn)  (*(sn) = SRC_NULL)
#define SN_IS_TERMINATOR(sn)    (*(sn) == SRC_NULL)

/*
 * Append a new source note of the given type (and therefore size) to cg's
 * notes dynamic array, updating cg->noteCount.  Return the new note's index
 * within the array pointed at by cg->notes.  Return -1 if out of memory.
 */
extern intN
js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type);

extern intN
js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
	       ptrdiff_t offset);

extern intN
js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
	       ptrdiff_t offset1, ptrdiff_t offset2);

/*
 * Get and set the offset operand identified by which (0 for the first, etc.).
 */
extern JS_FRIEND_API(ptrdiff_t)
js_GetSrcNoteOffset(jssrcnote *sn, uintN which);

extern JSBool
js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
		    uintN which, ptrdiff_t offset);

/*
 * Finish taking source notes in cx's tempPool by copying them to new
 * stable store allocated via JS_malloc.  Return null on malloc failure,
 * which means this function reported an error.
 */
extern jssrcnote *
js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg);

/*
 * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel)
 * from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount
 * js_NewTryNote calls.  The storage is freed by js_FinishCodeGenerator.
 */
extern JSBool
js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg);

/*
 * Grab the next trynote slot in cg, filling it in appropriately.
 */
extern JSTryNote *
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
	      ptrdiff_t end, ptrdiff_t catchStart);

/*
 * Finish generating exception information, and copy it to JS_malloc
 * storage.
 */
extern JSBool
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp);

JS_END_EXTERN_C

#endif /* jsemit_h___ */

**** End of jsemit.h. ****

**** Start of jsexn.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS standard exception implementation.
 */

#include "jsstddef.h"
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsexn.h"
#include "jsfun.h"

#if JS_HAS_ERROR_EXCEPTIONS
#if !JS_HAS_EXCEPTIONS
# error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS"
#endif

/* Declaration to resolve circular reference of exn_class by Exception. */
static JSBool
Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

static void
exn_finalize(JSContext *cx, JSObject *obj);

static JSClass exn_class = {
    "Exception",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   exn_finalize,
    NULL,             NULL,             NULL,             Exception
};

/*
 * A copy of the JSErrorReport originally generated.
 */
typedef struct JSExnPrivate {
    JSErrorReport *errorReport;
} JSExnPrivate;

/*
 * Copy everything interesting about an error into allocated memory.
 */
static JSExnPrivate *
exn_initPrivate(JSContext *cx, JSErrorReport *report)
{
    JSExnPrivate *newPrivate;
    JSErrorReport *newReport;

    newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate));

    /* Copy the error report */
    newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport));

    if (report->filename != NULL) {
	newReport->filename =
	    (const char *)JS_malloc(cx, strlen(report->filename)+1);
	strcpy((char *)newReport->filename, report->filename);
    } else {
	newReport->filename = NULL;
    }

    newReport->lineno = report->lineno;

    /*
     * We don't need to copy linebuf and tokenptr, because they
     * point into the deflated string cache.  (currently?)
     */
    newReport->linebuf = report->linebuf;
    newReport->tokenptr = report->tokenptr;

    /*
     * But we do need to copy uclinebuf, uctokenptr, because they're
     * pointers into internal tokenstream structs, and may go away.
     *
     * NOTE nothing uses this and I'm not really maintaining it until
     * I know it's the desired API.
     *
     * Temporarily disabled, because uclinebuf is 0x10 when I evaluate 'Math()'!
     */
#if 0
    if (report->uclinebuf != NULL) {
	len = js_strlen(report->uclinebuf) + 1;
	newReport->uclinebuf =
	    (const jschar *)JS_malloc(cx, len * sizeof(jschar));
	js_strncpy(newReport->uclinebuf, report->uclinebuf, len);
	newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr -
							report->uclinebuf);
    } else
#endif
	newReport->uclinebuf = newReport->uctokenptr = NULL;

    /* Executing the code below makes a seg fault where JS_malloc fails!? */
#if 0    
    if (report->ucmessage != NULL) {
        len = js_strlen(report->ucmessage) + 1;
        newReport->ucmessage = (const jschar *)JS_malloc(cx, len);
        js_strncpy((jschar *)newReport->ucmessage, report->ucmessage, len);
        
        if (report->messageArgs) {
            int i;
            
            for (i = 0; report->messageArgs[i] != NULL; i++)
                ;
            JS_ASSERT(i);
            newReport->messageArgs =
                (const jschar **)JS_malloc(cx, (i + 1) * sizeof(jschar *));
            for (i = 0; report->messageArgs[i] != NULL; i++) {
                len = js_strlen(report->messageArgs[i]) + 1;
                newReport->messageArgs[i] =
                    (const jschar *)JS_malloc(cx, len * sizeof(jschar));
                js_strncpy((jschar *)(newReport->messageArgs[i]),
                           report->messageArgs[i], len);
            }
            report->messageArgs[i] = NULL;
        } else {
            report->messageArgs = NULL;
        }
    } else {
        newReport->ucmessage = NULL;
        newReport->messageArgs = NULL;
    }
#else
    newReport->ucmessage = NULL;
    newReport->messageArgs = NULL;
#endif

    /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
    newReport->flags = report->flags;

    newPrivate->errorReport = newReport;
    return newPrivate;
}

/*
 * Undo all the damage done by exn_initPrivate.
 */
static void
exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData)
{
    JSErrorReport *report;
    const jschar **args;
    
    report = privateData->errorReport;
    JS_ASSERT(report);
    if (report->uclinebuf)
	JS_free(cx, (void *)report->uclinebuf);
    if (report->filename)
	JS_free(cx, (void *)report->filename);
    if (report->ucmessage)
        JS_free(cx, (void *)report->ucmessage);
    if (report->ucmessage)
	JS_free(cx, (void *)report->ucmessage);
    if (report->messageArgs) {
        args = report->messageArgs;
        while(*args++ != NULL)
            JS_free(cx, (void *)*args);
        JS_free(cx, (void *)report->messageArgs);
    }
    JS_free(cx, report);
    JS_free(cx, privateData);
}

static void
exn_finalize(JSContext *cx, JSObject *obj)
{
    JSExnPrivate *privateData;
    jsval private;

    private = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    if (private != JSVAL_NULL) {
        privateData = JSVAL_TO_PRIVATE(private);
        if (privateData)
            exn_destroyPrivate(cx, privateData);
    }
}

static JSErrorReport *
js_ErrorFromException(JSContext *cx, jsval exn)
{
    JSObject *obj;
    JSExnPrivate *privateData;
    jsval private;

    if (!JSVAL_IS_OBJECT(exn))
        return NULL;
    obj = JSVAL_TO_OBJECT(exn);
    if (OBJ_GET_CLASS(cx, obj) != &exn_class)
        return NULL;
    private = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    privateData = JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
    if (!privateData)
        return NULL;
    
    JS_ASSERT(privateData->errorReport);
    return privateData->errorReport;
}

/* This must be kept in synch with the exceptions array below. */
typedef enum JSExnType {
    JSEXN_NONE = -1,
    JSEXN_EXCEPTION,
      JSEXN_ERR,
	JSEXN_INTERNALERR,
	JSEXN_SYNTAXERR,
	JSEXN_REFERENCEERR,
	JSEXN_CALLERR,
	  JSEXN_TARGETERR,
	JSEXN_CONSTRUCTORERR,
	JSEXN_CONVERSIONERR,
	  JSEXN_TOOBJECTERR,
	  JSEXN_TOPRIMITIVEERR,
	  JSEXN_DEFAULTVALUEERR,
	JSEXN_ARRAYERR,
	JSEXN_LIMIT
} JSExnType;

struct JSExnSpec {
    int protoIndex;
    const char *name;
};

static struct JSExnSpec exceptions[] = {
    { JSEXN_NONE,          "Exception"         },
    { JSEXN_EXCEPTION,     "Error"             },
    { JSEXN_ERR,           "InternalError"     },
    { JSEXN_ERR,           "SyntaxError"       },
    { JSEXN_ERR,           "ReferenceError"    },
    { JSEXN_ERR,           "CallError"         },
    { JSEXN_CALLERR,       "TargetError"       },
    { JSEXN_ERR,           "ConstructorError"  },
    { JSEXN_ERR,           "ConversionError"   },
    { JSEXN_CONVERSIONERR, "ToObjectError"     },
    { JSEXN_CONVERSIONERR, "ToPrimitiveError"  },
    { JSEXN_CONVERSIONERR, "DefaultValueError" },
    { JSEXN_ERR,           "ArrayError"        },
    {0}
};

static JSBool
Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *message;

    /*
     * Does it make sense to throw a not-a-function-error here?
     * A constructor that was not a function would be novel.
     */
    if (!cx->fp->constructing) {
	return JS_TRUE;
    }

    /*
     * If it's a new object of class Exception, then null out the private
     * data so that the finalizer doesn't attempt to free it.
     */
    if (OBJ_GET_CLASS(cx, obj) == &exn_class)
        OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_NULL);

    /*
     * Set the 'message' property.
     */
    if (argc > 0) {
	message = js_ValueToString(cx, argv[0]);
	if (!message)
	    return JS_FALSE;
    } else {
	message = cx->runtime->emptyString;
    }
    if (!JS_DefineProperty(cx, obj, "message", STRING_TO_JSVAL(message),
                           NULL, NULL, JSPROP_ENUMERATE));

    return JS_TRUE;
}

/*
 * Convert to string.
 *
 * This method only uses JavaScript-modifiable properties name, message; it is
 * left to the host to check for private data and report filename and line
 * number information along with this message.
 */
static JSBool
exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval v;
    JSString *name, *message, *result;
    jschar *chars, *cp;
    size_t length;
    jsid id;

    id = (jsid) cx->runtime->atomState.nameAtom;
    if (!OBJ_GET_PROPERTY(cx, obj, id, &v) || !(name = js_ValueToString(cx, v)))
        return JS_FALSE;

    if (!JS_GetProperty(cx, obj, "message", &v) ||
        !(message = js_ValueToString(cx, v)))
        return JS_FALSE;

    if (message->length > 0) {
        length = name->length + message->length + 2;
        cp = chars = JS_malloc(cx, (length + 1) * sizeof(jschar));
        if (!chars)
            return JS_FALSE;
        
        js_strncpy(cp, name->chars, name->length);
        cp += name->length;
        *cp++ = ':'; *cp++ = ' ';
        js_strncpy(cp, message->chars, message->length);
        cp += message->length;
        *cp = 0;
        
        result = js_NewString(cx, chars, length, 0);
        if (!result) {
            JS_free(cx, chars);
            return JS_FALSE;
        }
    } else {
        result = name;
    }

    *rval = STRING_TO_JSVAL(result);
    return JS_TRUE;
}

#if JS_HAS_TOSOURCE
/*
 * Return a string that may eval to something similar to the original object.
 */
static JSBool
exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *name, *message, *result;
    jschar *chars, *cp;
    jsid id;
    size_t length;
    jsval v;

    id = (jsid) cx->runtime->atomState.nameAtom;
    if (!OBJ_GET_PROPERTY(cx, obj, id, &v) || !(name = js_ValueToString(cx, v)))
        return JS_FALSE;

    if (!JS_GetProperty(cx, obj, "message", &v) ||
        !(message = js_ValueToString(cx, v)))
        return JS_FALSE;

    length = (message->length > 0) ? name->length + message->length + 10
        : name->length + 8;

    cp = chars = JS_malloc(cx, (length + 1) * sizeof(jschar));
    if (!chars)
        return JS_FALSE;

    *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
    js_strncpy(cp, name->chars, name->length);
    cp += name->length;
    *cp++ = '(';
    if (message->length > 0) {
        *cp++ = '"';
        js_strncpy(cp, message->chars, message->length);
        cp += message->length;
        *cp++ = '"';
    }
    *cp++ = ')'; *cp++ = ')'; *cp = 0;

    result = js_NewString(cx, chars, length, 0);
    if (!result) {
        JS_free(cx, chars);
        return JS_FALSE;
    }

    *rval = STRING_TO_JSVAL(result);
    return JS_TRUE;
}
#endif

static JSFunctionSpec exception_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   exn_toSource,           0},
#endif
    {js_toString_str,   exn_toString,           0},
    {0}
};

JSObject *
js_InitExceptionClasses(JSContext *cx, JSObject *obj)
{
    int i;
    JSObject *protos[JSEXN_LIMIT];

    /* Initialize the prototypes first. */
    for (i = 0; exceptions[i].name != 0; i++) {
        JSAtom *atom;
        JSFunction *fun;
        JSString *nameString;
        int protoidx = exceptions[i].protoIndex;
        
        /* Make the prototype for the current constructor name. */
        protos[i] = js_NewObject(cx, &exn_class,
                                 protoidx >= 0 ? protos[protoidx] : NULL,
                                 obj);
        if (!protos[i])
            return NULL;

        atom = js_Atomize(cx, exceptions[i].name, strlen(exceptions[i].name), 0);

        /* Make a constructor function for the current name. */
        fun = js_DefineFunction(cx, obj, atom, Exception, 1, 0);
        if (!fun)
            return NULL; /* XXX also remove named property from obj... */

        /* Make this constructor make objects of class Exception. */
        fun->clasp = &exn_class;

        /* Make the prototype and constructor links. */
        if (!js_SetClassPrototype(cx, fun->object, protos[i],
                                  JSPROP_READONLY | JSPROP_PERMANENT))
            return NULL;

        /* proto bootstrap bit from JS_InitClass omitted. */
        nameString = JS_NewStringCopyZ(cx, exceptions[i].name);
        if (nameString == NULL)
            return NULL;
        
        /* Add the name property to the prototype. */
        if (!JS_DefineProperty(cx, protos[i], js_name_str,
                               STRING_TO_JSVAL(nameString),
                               NULL, NULL,
                               JSPROP_ENUMERATE)) {
            /* release it? */
            return NULL;
        }   
        
        /* So finalize knows whether to. */
        OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_NULL);
    }
    
    /*
     * Add an empty message property.  (To Exception.prototype only,
     * because this property will be the same for all the exception
     * protos.)
     */
    if (!JS_DefineProperty(cx, protos[0], "message",
                           STRING_TO_JSVAL(cx->runtime->emptyString),
                           NULL, NULL, JSPROP_ENUMERATE))
        return NULL;
    
    /*
     * Add methods only to Exception.prototype, because ostensibly all
     * exception types delegate to that.
     */
    if (!JS_DefineFunctions(cx, protos[0], exception_methods))
        return NULL;

    return protos[0];
}

static JSExnType errorToExceptionNum[] = {
#define MSG_DEF(name, number, count, exception, format) \
    exception,
#include "js.msg"
#undef MSG_DEF
};

#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
/* For use below... get character strings for error name and exception name */
static struct exnname { char *name; char *exception; } errortoexnname[] = {
#define MSG_DEF(name, number, count, exception, format) \
    {#name, #exception},
#include "js.msg"
#undef MSG_DEF
};
#endif /* DEBUG */

JSBool
js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
{
    JSErrNum errorNumber;
    JSObject *errObject, *errProto;
    JSExnType exn;
    JSExnPrivate *privateData;
    JSString *msgstr;

    /* Find the exception index associated with this error. */
    JS_ASSERT(reportp);
    errorNumber = reportp->errorNumber;
    exn = errorToExceptionNum[errorNumber];
    JS_ASSERT(exn < JSEXN_LIMIT);

#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
    /* Print the error name and the associated exception name to stderr */
    fprintf(stderr, "%s\t%s\n",
	    errortoexnname[errorNumber].name,
	    errortoexnname[errorNumber].exception);
#endif

    /*
     * Return false (no exception raised) if no exception is associated
     * with the given error number.
     */
    if (exn == JSEXN_NONE)
	return JS_FALSE;

    /*
     * Try to get an appropriate prototype by looking up the corresponding
     * exception constructor name in the current context.  If the constructor
     * has been deleted or overwritten, this may fail or return NULL, and
     * js_NewObject will fall back to using Object.prototype.
     */
    if (!js_GetClassPrototype(cx, exceptions[exn].name, &errProto))
        errProto = NULL;

    /*
     * Use js_NewObject instead of js_ConstructObject, because
     * js_ConstructObject seems to require a frame.
     */
    errObject = js_NewObject(cx, &exn_class, errProto, NULL);

    /* Store 'message' as a javascript-visible value. */
    msgstr = JS_NewStringCopyZ(cx, message);
    if (!JS_DefineProperty(cx, errObject, "message", STRING_TO_JSVAL(msgstr),
                           NULL, NULL,
                           JSPROP_ENUMERATE))
        return JS_FALSE;

    /*
     * Construct a new copy of the error report, and store it in the
     * exception objects' private data.  We can't use the error report
     * handed in, because it's stack-allocated, and may point to transient
     * data in the JSTokenStream.
     */
    privateData = exn_initPrivate(cx, reportp);
    OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData));

    /* Set the generated Exception object as the current exception. */
    JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));

    /* Flag the error report passed in to indicate an exception was raised. */
    reportp->flags |= JSREPORT_EXCEPTION;

    return JS_TRUE;
}
#endif /* JS_HAS_ERROR_EXCEPTIONS */

#if JS_HAS_EXCEPTIONS

extern JSBool
js_ReportUncaughtException(JSContext *cx)
{
    JSObject *exnObject;
    JSString *str;
    jsval exn;
    JSErrorReport *reportp;
    
    if (!JS_IsExceptionPending(cx))
        return JS_FALSE;
    
    if (!JS_GetPendingException(cx, &exn))
        return JS_FALSE;

    /*
     * Because js_ValueToString below could error and an exception object
     * could become unrooted, we root it here.
     */
    if (JSVAL_IS_OBJECT(exn)) {
        exnObject = JSVAL_TO_OBJECT(exn);
        if (!js_AddRoot(cx, exnObject, "exn.report.root"))
            return JS_FALSE;
    } else {
        exnObject = NULL;
    }
    str = js_ValueToString(cx, exn);

#if JS_HAS_ERROR_EXCEPTIONS
    reportp = js_ErrorFromException(cx, exn);
#else
    reportp = NULL;
#endif

    if (reportp == NULL) {
        /*
         * XXXmccabe todo: Instead of doing this, synthesize an error report
         * struct that includes the filename, lineno where the exception was
         * originally thrown.
         */
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                             JSMSG_UNCAUGHT_EXCEPTION, js_GetStringBytes(str));
    } else {
        /* Flag the error as an exception. */
        reportp->flags |= JSREPORT_EXCEPTION;
        js_ReportErrorAgain(cx, js_GetStringBytes(str), reportp);
    }

    if (exnObject != NULL)
        js_RemoveRoot(cx, exnObject);
    return JS_TRUE;
}

#endif	    /* JS_HAS_EXCEPTIONS */

**** End of jsexn.c. ****

**** Start of jsexn.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS runtime exception classes.
 */

#ifndef jsexn_h___
#define jsexn_h___

JS_BEGIN_EXTERN_C

/*
 * Initialize exception object hierarchy.
 */
extern JSObject *
js_InitExceptionClasses(JSContext *cx, JSObject *obj);

/*
 * Given a JSErrorReport, check to see if there is an exception associated with
 * the error number.  If there is, then create an appropriate exception object,
 * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the
 * error report.  Exception-aware host error reporters should probably ignore
 * error reports so flagged.  Returns JS_TRUE if an associated exception is
 * found and set, JS_FALSE otherwise..
 */
extern JSBool
js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp);

/*
 * Called if an api call to js_Execute or js_CallFunctionValue fails; calls the
 * error reporter with the error report associated with any uncaught exception
 * that has been raised.  Returns true if there was an exception pending, and
 * the error reporter was actually called.
 *
 * The JSErrorReport * that the error reporter is called with is currently
 * associated with a JavaScript object, and is not guaranteed to persist after
 * the object is collected.  Any persistent uses of the JSErrorReport contents
 * should make their own copy.
 *
 * The flags field of the JSErrorReport will have the JSREPORT_EXCEPTION flag
 * set; embeddings that want to silently propagate JavaScript exceptions to
 * other contexts may want to use an error reporter that ignores errors with
 * this flag.
 */
extern JSBool
js_ReportUncaughtException(JSContext *cx);

JS_END_EXTERN_C

#endif /* jsexn_h___ */

**** End of jsexn.h. ****

**** Start of jsfile.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS File object
 */

#ifndef _WINDOWS
#  include <strings.h>
#  include <stdio.h>
#  include <stdlib.h>
#  include <unistd.h>
#else
#  include "direct.h"
#endif

#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsdate.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jsutil.h" /* Added by JSIFY */

#if JS_HAS_FILE_OBJECT

/* NSPR dependencies */
#include "prio.h"
#include "prerror.h"
#include "jsutil.h" /* Added by JSIFY */

/* bunch of defines. */

#define FILE_CONSTRUCTOR_UNDEFINED_ERROR "No File constructor defined!"
#define FILE_CURRENTDIR_UNDEFINED_ERROR "File.currentDir is undefined."
#define FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR "The first argument to file.open must be a string."
#define SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR "The second argument to file.open must be a string."
#define CANNOT_COPY_OPENED_FILE_ERROR "Cannot copy an opened file."
#define CANNOT_ACCESS_FILE_INFO_ERROR "Cannot access this file informations"
#define COPY_READ_ERROR "An error occured while attempting to read a file to copy."
#define COPY_WRITE_ERROR "An error occured while attempting to copy into a file."
#define RENAME_EXPECTS_ONE_ARG_ERROR "file.renameTo expects one argument."
#define CANNOT_FLUSH_CLOSE_FILE_ERROR "Cannot flush a non-opened file."
#define CANNOT_OPEN_WRITING_ERROR "Cannot open file for writing."
#define WRITEALL_EXPECTS_ONE_ARG_ERROR "Wrong number of argument passed to writeAll"
#define FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR "writeAll expects an array for argument."
#define CANNOT_OPEN_FILE_ERROR "Cannot open file"
#define FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR "The argument to the File constructor must be a string."

#define SPECIAL_FILE_STRING "Special File"
#define CURRENTDIR_PROPERTY "currentDir"
#define FILE_CONSTRUCTOR "File"

#define ASCII   0
#define UTF8    1
#define UCS2    2

#define asciistring   "ascii"
#define utfstring     "utf"
#define unicodestring "unicode"


/* Platform dependent code goes here. */

#define MAX_PATH_LENGTH 1024 /* XXX dirty */

#ifdef XP_MAC
#  define LINEBREAK             "\012"
#  define LINEBREAK_LEN 1
#  define FILESEPARATOR         ':'
#  define FILESEPARATOR2        '\0'
#  define CURRENT_DIR "HARD DISK:Desktop Folder"

static JSBool
isAbsolute(char*name)
{
    return (name[0]!=FILESEPARATOR);
}

/* We assume: base is a valid absolute path, not ending with : 
          name is a valid relative path. */
static char * combinePath(JSContext *cx, char *base, char*name)
{
    tmp = (char*)JS_malloc(cx, strlen(base)+strlen(name)+2);
    if (!tmp) {
    JS_ReportOutOfMemory(cx);
    return NULL;
    }
    strcpy(tmp,base);
    i=strlen(base)-1;
    if (base[i]!=FILESEPARATOR) {
      tmp[i+1]=FILESEPARATOR;
      tmp[i+2]='\0';
    }
    strcat(tmp,name);
    return tmp;
}

/* returned string must be freed */
static char *
fileBaseName(JSContext *cx, char * pathname)
{
    jsint index,aux;
    char *basename;

    index=strlen(pathname)-1;
    aux=index;
    while ((index>=0)&&(pathname[index]!=FILESEPARATOR)) index--;
    basename = (char*)JS_malloc(cx, aux-index+1);
    if (!basename) {
        JS_ReportOutOfMemory(cx);
        return NULL;
    }
    strncpy(basename,&pathname[index+1],aux-index);
    basename[aux-index]='\0';
    return basename;
}

/* returned string must be freed */
static char *
fileDirectoryName(JSContext *cx, char * pathname)
{
    jsint index;
    char *dirname;

    index=strlen(pathname)-1;
    while ((index>0)&&(pathname[index]!=FILESEPARATOR)) index--;

    if (index>=0) {
        dirname=(char*)JS_malloc(cx,index+2);
        if (!dirname) {
            JS_ReportOutOfMemory(cx);
            return NULL;
        }
        strncpy(dirname,pathname,index);
        dirname[index]=FILESEPARATOR;
        dirname[index+1]='\0';
    } else
        dirname=JS_strdup(cx,pathname);
    return dirname;
}

#else
#  if defined(_WINDOWS) || defined(OS2)
#    define LINEBREAK           "\015\012"
#    define LINEBREAK_LEN       2
#    define FILESEPARATOR       '\\'
#    define FILESEPARATOR2      '/'
#    define CURRENT_DIR "c:\\"

static JSBool
isAbsolute(char*name)
{
    if (strlen(name)<2) 
    return JS_FALSE;

    if (name[1]==':')
    return JS_TRUE;

    return JS_FALSE; /* First approximation, ignore "/tmp" case.. */
}

/* We assume: base is a valid absolute path, starting with x:, ending with /. 
          name is a valid relative path, and may have a drive selector. */
static char * combinePath(JSContext *cx, char *base, char*name)
{
    jsint i;
    char *tmp, *tmp1;

    if ((strlen(name)>=2)&&(name[1]==':')) { /* remove a drive selector if there's one.*/
    tmp1=&name[2];
    } else
    tmp1=name;

    tmp = (char*)JS_malloc(cx, strlen(base)+strlen(tmp1)+2);
    if (!tmp) {
    JS_ReportOutOfMemory(cx);
    return NULL;
    }
    strcpy(tmp,base);
    i=strlen(base)-1;
    if ((base[i]!=FILESEPARATOR)&&(base[i]!=FILESEPARATOR2)) {
      tmp[i+1]=FILESEPARATOR;
      tmp[i+2]='\0';
    }
    strcat(tmp,tmp1);
    return tmp;
    
}

/* returned string must be freed */
static char *
fileBaseName(JSContext *cx, char * pathname)
{
    jsint index,aux;
    char *basename;

    /* First, get rid of the drive selector */
    if ((strlen(pathname)>=2)&&(pathname[1]==':')) {
        pathname=&pathname[2];
    }

    index=strlen(pathname)-1;
    while ((index>0)&&((pathname[index]==FILESEPARATOR)||
                       (pathname[index]==FILESEPARATOR2))) index--;
    aux=index;
    while ((index>=0)&&(pathname[index]!=FILESEPARATOR)&&
                      (pathname[index]!=FILESEPARATOR2)) index--;
    basename = (char*)JS_malloc(cx, aux-index+3);
    if (!basename) {
        JS_ReportOutOfMemory(cx);
        return NULL;
    }
    strncpy(basename,&pathname[index+1],aux-index);
    basename[aux-index]='\0';
    return basename;
}

/* returned string must be freed */
static char *
fileDirectoryName(JSContext *cx, char * pathname)
{
    jsint index;
    char *dirname;
    char *backpathname;
    char drive='\0';

    backpathname=pathname;
    /* First, get rid of the drive selector */
    if ((strlen(pathname)>=2)&&(pathname[1]==':')) {
        drive=pathname[0];
        pathname=&pathname[2];
    }

    index=strlen(pathname)-1;
    while ((index>0)&&((pathname[index]==FILESEPARATOR)||
                       (pathname[index]==FILESEPARATOR2))) index--;
    while ((index>0)&&(pathname[index]!=FILESEPARATOR)&&
                      (pathname[index]!=FILESEPARATOR2)) index--;

    if (index>=0) {
        dirname=(char*)JS_malloc(cx,index+4);
        if (!dirname) {
            JS_ReportOutOfMemory(cx);
            return NULL;
        }
        if (drive!='\0') {
            dirname[0]=toupper(drive);
            dirname[1]=':';
            strncpy(&dirname[2],pathname,index);
            dirname[index+2]=FILESEPARATOR;
            dirname[index+3]='\0';
        } else {
            strncpy(dirname,pathname,index);
            dirname[index]=FILESEPARATOR;
            dirname[index+1]='\0';
        }
    } else
        dirname=JS_strdup(cx,backpathname); /* may include drive selector */

    return dirname;
}

#  else
#    ifdef XP_UNIX
#      define LINEBREAK         "\012"
#      define LINEBREAK_LEN     1
#      define FILESEPARATOR     '/'
#      define FILESEPARATOR2    '\0'
#      define CURRENT_DIR "/"

static JSBool
isAbsolute(char*name)
{
    return (name[0]==FILESEPARATOR);
}

/* We assume: base is a valid absolute path[, ending with /]. name is a valid relative path. */
static char * combinePath(JSContext *cx, char *base, char*name)
{
    jsint i;
    char * tmp;
    tmp = (char*)JS_malloc(cx, strlen(base)+strlen(name)+2);
    if (!tmp) {
    JS_ReportOutOfMemory(cx);
    return NULL;
    }
    strcpy(tmp,base);
    i=strlen(base)-1;
    if (base[i]!=FILESEPARATOR) {
      tmp[i+1]=FILESEPARATOR;
      tmp[i+2]='\0';
    }
    strcat(tmp,name);
    return tmp;
}

/* returned string must be freed */
static char *
fileBaseName(JSContext *cx, char * pathname)
{
    jsint index,aux;
    char *basename;

    index=strlen(pathname)-1;
    while ((index>0)&&((pathname[index]==FILESEPARATOR)||
                       (pathname[index]==FILESEPARATOR2))) index--;
    aux=index;
    while ((index>=0)&&(pathname[index]!=FILESEPARATOR)&&
                      (pathname[index]!=FILESEPARATOR2)) index--;
    basename = (char*)JS_malloc(cx, aux-index+1);
    if (!basename) {
        JS_ReportOutOfMemory(cx);
        return NULL;
    }
    strncpy(basename,&pathname[index+1],aux-index);
    basename[aux-index]='\0';
    return basename;
}

/* returned string must be freed */
static char *
fileDirectoryName(JSContext *cx, char * pathname)
{
    jsint index;
    char *dirname;

    index=strlen(pathname)-1;
    while ((index>0)&&((pathname[index]==FILESEPARATOR)||
                       (pathname[index]==FILESEPARATOR2))) index--;
    while ((index>0)&&(pathname[index]!=FILESEPARATOR)&&
                      (pathname[index]!=FILESEPARATOR2)) index--;

    if (index>=0) {
        dirname=(char*)JS_malloc(cx,index+2);
        if (!dirname) {
            JS_ReportOutOfMemory(cx);
            return NULL;
        }
        strncpy(dirname,pathname,index);
        dirname[index]=FILESEPARATOR;
        dirname[index+1]='\0';
    } else
        dirname=JS_strdup(cx,pathname);
    return dirname;
}

#    endif /* UNIX */
#  endif /* WIN */
#endif /* MAC */


/* returned string must be freed.. */
static char *
absolutePath(JSContext *cx, char * path)
{
    JSObject *obj;
    JSString *str;
    jsval prop;

    if (isAbsolute(path))
    return JS_strdup(cx,path);
    else {
      /* */
        obj=JS_GetGlobalObject(cx);
        if (!JS_GetProperty(cx,obj,FILE_CONSTRUCTOR, &prop)) {
            JS_ReportError(cx,FILE_CONSTRUCTOR_UNDEFINED_ERROR);
            return JS_strdup(cx,path);
        }
        obj=JSVAL_TO_OBJECT(prop);
        if (!JS_GetProperty(cx,obj,CURRENTDIR_PROPERTY, &prop)) {
            JS_ReportError(cx,FILE_CURRENTDIR_UNDEFINED_ERROR);
            return JS_strdup(cx,path);
        }
        str=JS_ValueToString(cx, prop);
        if (!str ) {
            return JS_strdup(cx,path);
        }
        return combinePath(cx, JS_GetStringBytes(str), path); /* should we have an array of current dirs indexed by drive for windows? */
    }
}

/* this function should be in the platform specific side. */
/* :: is not handled. */
static char *
canonicalPath(JSContext *cx, char * path) 
{
    char *tmp1, *tmp;
    char *base, *dir, *current, *result;
    jsint c;
    jsint back=0;

    if (!isAbsolute(path))
        tmp1=absolutePath(cx, path);
    else
        tmp1=JS_strdup(cx,path);
    
    result=JS_strdup(cx,"");

    current=tmp1;

    base=fileBaseName(cx, current);
    dir=fileDirectoryName(cx, current);
    while (strcmp(dir,current)) {
        if (!strcmp(base,"..")) {
            back++;
        } else
        if (!strcmp(base,".")) {
        } else {
            if (back>0) 
                back--;
            else {
                tmp=result;
                result=JS_malloc(cx,strlen(base)+1+strlen(tmp)+1);
                if (!result) {
                    JS_ReportOutOfMemory(cx);
                    JS_free(cx,dir);
                    JS_free(cx,base);
                    JS_free(cx,current);
                    return NULL;
                }
                strcpy(result,base);
                c=strlen(result);
                if (strlen(tmp)>0) {
                    result[c]=FILESEPARATOR;
                    result[c+1]='\0';
                    strcat(result,tmp);
                }
                JS_free(cx,tmp);
            }
        }
        JS_free(cx,current);
        JS_free(cx,base);
        current=dir;
        base= fileBaseName(cx, current);
        dir=fileDirectoryName(cx, current);
    }
    tmp=result;
    result=JS_malloc(cx,strlen(dir)+1+strlen(tmp)+1);
    if (!result) {
        JS_ReportOutOfMemory(cx);
        JS_free(cx,dir);
        JS_free(cx,base);
        JS_free(cx,current);
        return NULL;
    }
    strcpy(result,dir);
    c=strlen(result);
    if (strlen(tmp)>0) {
        if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
            result[c]=FILESEPARATOR;
            result[c+1]='\0';
        }
        strcat(result,tmp);
    }
    JS_free(cx,tmp);
    JS_free(cx,dir);
    JS_free(cx,base);
    JS_free(cx,current);

    return result;
}


/*-------------------------------------*/
/* The following is ripped from libi18n/unicvt.c and include files.. */

/*
 * UTF8 defines and macros
 */
#define ONE_OCTET_BASE          0x00    /* 0xxxxxxx */
#define ONE_OCTET_MASK          0x7F    /* x1111111 */
#define CONTINUING_OCTET_BASE   0x80    /* 10xxxxxx */
#define CONTINUING_OCTET_MASK   0x3F    /* 00111111 */
#define TWO_OCTET_BASE          0xC0    /* 110xxxxx */
#define TWO_OCTET_MASK          0x1F    /* 00011111 */
#define THREE_OCTET_BASE        0xE0    /* 1110xxxx */
#define THREE_OCTET_MASK        0x0F    /* 00001111 */
#define FOUR_OCTET_BASE         0xF0    /* 11110xxx */
#define FOUR_OCTET_MASK         0x07    /* 00000111 */
#define FIVE_OCTET_BASE         0xF8    /* 111110xx */
#define FIVE_OCTET_MASK         0x03    /* 00000011 */
#define SIX_OCTET_BASE          0xFC    /* 1111110x */
#define SIX_OCTET_MASK          0x01    /* 00000001 */

#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK  ) == ONE_OCTET_BASE)
#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK  ) == TWO_OCTET_BASE)
#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK  ) == SIX_OCTET_BASE)
#define IS_UTF8_2ND_THRU_6TH(x) \
                    (( (x)&~CONTINUING_OCTET_MASK  ) == CONTINUING_OCTET_BASE)
#define IS_UTF8_1ST_OF_UCS2(x) \
            IS_UTF8_1ST_OF_1(x) \
            || IS_UTF8_1ST_OF_2(x) \
            || IS_UTF8_1ST_OF_3(x)


#define MAX_UCS2            0xFFFF   
#define DEFAULT_CHAR        0x003F  /* Default char is "?" */ 
#define BYTE_MASK           0xBF
#define BYTE_MARK           0x80


/* Function: one_ucs2_to_utf8_char
 * 
 * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
 * We need a UTF-8 buffer because we don't know before this 
 * function how many bytes of utf-8 data will be written. It also
 * takes a pointer to the end of the UTF-8 buffer so that we don't
 * overwrite data. This function returns the number of UTF-8 bytes
 * of data written, or -1 if the buffer would have been overrun.
 */


#define LINE_SEPARATOR      0x2028
#define PARAGRAPH_SEPARATOR 0x2029
static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, 
        unsigned char *tobufendp, uint16 onechar)

{

     int16 numUTF8bytes = 0;

    if((onechar == LINE_SEPARATOR)||(onechar == PARAGRAPH_SEPARATOR))
    {
        strcpy((char*)tobufp, "\n");
        return strlen((char*)tobufp);;
    }

        if (onechar < 0x80) {               numUTF8bytes = 1;
        } else if (onechar < 0x800) {       numUTF8bytes = 2;
        } else if (onechar <= MAX_UCS2) {   numUTF8bytes = 3;
        } else { numUTF8bytes = 2;
                 onechar = DEFAULT_CHAR;
        } 
                
        tobufp += numUTF8bytes;

        /* return error if we don't have space for the whole character */
        if (tobufp > tobufendp) {
            return(-1); 
        }


        switch(numUTF8bytes) {

            case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
                    *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
                    *--tobufp = onechar |  THREE_OCTET_BASE;
                    break;

            case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
                    *--tobufp = onechar | TWO_OCTET_BASE;
                    break;
            case 1: *--tobufp = (unsigned char)onechar;  break;
        }

        return(numUTF8bytes);
}


/*
 * utf8_to_ucs2_char
 *
 * Convert a utf8 multibyte character to ucs2
 *
 * inputs: pointer to utf8 character(s)
 *         length of utf8 buffer ("read" length limit)
 *         pointer to return ucs2 character
 *
 * outputs: number of bytes in the utf8 character
 *          -1 if not a valid utf8 character sequence
 *          -2 if the buffer is too short
 */
static int16
utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
{
    uint16 lead, cont1, cont2;

    /*
     * Check for minimum buffer length
     */
    if ((buflen < 1) || (utf8p == NULL)) {
        return -2;
    }
    lead = (uint16) (*utf8p);

    /*
     * Check for a one octet sequence
     */
    if (IS_UTF8_1ST_OF_1(lead)) {
        *ucs2p = lead & ONE_OCTET_MASK;
        return 1;
    }

    /*
     * Check for a two octet sequence
     */
    if (IS_UTF8_1ST_OF_2(*utf8p)) {
        if (buflen < 2)
            return -2;
        cont1 = (uint16) *(utf8p+1);
        if (!IS_UTF8_2ND_THRU_6TH(cont1))
            return -1;
        *ucs2p =  (lead & TWO_OCTET_MASK) << 6;
        *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
        return 2;
    }

    /*
     * Check for a three octet sequence
     */
    else if (IS_UTF8_1ST_OF_3(lead)) {
        if (buflen < 3)
            return -2;
        cont1 = (uint16) *(utf8p+1);
        cont2 = (uint16) *(utf8p+2);
        if (   (!IS_UTF8_2ND_THRU_6TH(cont1))
            || (!IS_UTF8_2ND_THRU_6TH(cont2)))
            return -1;
        *ucs2p =  (lead & THREE_OCTET_MASK) << 12;
        *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
        *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
        return 3;
    }
    else { /* not a valid utf8/ucs2 character */
        return -1;
    }
}

/*-------------------------------------*/

/* a few forward declarations.. */
static JSClass file_class;
JS_PUBLIC_API(JSObject*) NewFileObject(JSContext *cx, char*bytes);
static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);


typedef struct JSFile {
    char        *path;  /* the path to the file. */
    PRFileDesc* handle; /* the handle for the file, if opened.  */
    FILE*       nativehandle; /* native handle, for stuff NSPR doesn't do. */
    JSBool      opened;
    JSString    *linebuffer; /* temp buffer used by readln. */
    int32       mode;   /* mode used to open the file: read, write, append, create, etc.. */
    int32       type;   /* Asciiz, utf, unicode */
    char        byteBuffer[3]; /* bytes read in advance by JS_FileRead ( UTF8 encoding ) */
    jsint         nbBytesInBuf; /* number of bytes stored in the buffer above */
    jschar      charBuffer; /* character read in advance by readln ( mac files only ) */
    JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */
    JSBool      randomAccess; /* can the file be randomly accessed? false for stdin, and 
                                 UTF-encoded files. */
    JSBool      autoflush;   /* should we force a flush for each line break? */
} JSFile;


static void 
resetBuffers(JSFile * f) 
{
    f->charBufferUsed=JS_FALSE;
    f->nbBytesInBuf=0;
}


/* Helper function. buffered version of PR_Read. used by JS_FileRead */
static int32
JS_BufferedRead(JSFile * f,char*buf, int32 len) 
{
    int32 count=0;
    while (f->nbBytesInBuf>0&&len>0) {
        buf[0]=f->byteBuffer[0];
        f->byteBuffer[0]=f->byteBuffer[1];
        f->byteBuffer[1]=f->byteBuffer[2];
        f->nbBytesInBuf--;
        len--;
        buf+=1;
        count++;
    }
    if (len>0) {
        if (f->handle) {
            count+=PR_Read(f->handle, buf, len);
        } else {
            count+=fread(buf, 1, len, f->nativehandle);
        }
    }
    return count;
}


static int32
JS_FileRead(JSContext *cx, JSFile * f,jschar*buf,int32 len,int32 mode) 
{
    unsigned char*aux;
    int32 count,i;
    jsint remainder;
    unsigned char utfbuf[3];

    if (f->charBufferUsed) {
      buf[0]=f->charBuffer;
      buf++;
      len--;
      f->charBufferUsed=JS_FALSE;
    }

    switch (mode) {
    case ASCII:
      aux = (unsigned char*)JS_malloc(cx, len);
      if (!aux) {
        JS_ReportOutOfMemory(cx);
        return 0;
      }
      count=JS_BufferedRead(f,aux,len);
      if (count==-1) {
        JS_free(cx, aux);
        return 0;
      }
      for (i=0;i<len;i++) {
        buf[i]=(jschar)aux[i];
      }
      JS_free(cx, aux);
      break;
    case UTF8:
        remainder = 0;
        for (count=0;count<len;count++) {
            i=JS_BufferedRead(f,utfbuf+remainder,3-remainder);
            if (i<=0) {
                return count;
            }
            i=utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
            if (i<0) {
                return count;
            } else {
                if (i==1) {
                    utfbuf[0]=utfbuf[1];
                    utfbuf[1]=utfbuf[2];
                    remainder=2;
                } else
                if (i==2) {
                    utfbuf[0]=utfbuf[2];
                    remainder=1;
                } else
                if (i==3)
                    remainder=0;
            }
        }
        while (remainder>0) {
            f->byteBuffer[f->nbBytesInBuf]=utfbuf[0];
            f->nbBytesInBuf++;
            utfbuf[0]=utfbuf[1];
            utfbuf[1]=utfbuf[2];
            remainder--;
        }
      break;
    case UCS2:
      count=JS_BufferedRead(f,(char*)buf,len*2)>>1;
      if (count==-1) {
          return 0;
      }
      break;
    }
    return count;
}

static int32
JS_FileSkip(JSFile * f, int32 len, int32 mode) 
{
    int32 count,i;
    jsint remainder;
    unsigned char utfbuf[3];
    jschar tmp;

    switch (mode) {
    case ASCII:
        if (f->handle) {
            count=PR_Seek(f->handle, len, PR_SEEK_CUR);
        } else {
            count=fseek(f->nativehandle, len, SEEK_CUR);
        }
        break;
    case UTF8:
        remainder = 0;
        for (count=0;count<len;count++) {
            i=JS_BufferedRead(f,utfbuf+remainder,3-remainder);
            if (i<=0) {
                return 0;
            }
            i=utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
            if (i<0) {
                return 0;   
            } else {
                
                if (i==1) {
                    utfbuf[0]=utfbuf[1];
                    utfbuf[1]=utfbuf[2];
                    remainder=2;
                } else
                if (i==2) {
                    utfbuf[0]=utfbuf[2];
                    remainder=1;
                } else
                if (i==3)
                    remainder=0;
            }
        }
        while (remainder>0) {
            f->byteBuffer[f->nbBytesInBuf]=utfbuf[0];
            f->nbBytesInBuf++;
            utfbuf[0]=utfbuf[1];
            utfbuf[1]=utfbuf[2];
            remainder--;
        }
      break;
    case UCS2:
        if (f->handle) {
            count=PR_Seek(f->handle, len*2, PR_SEEK_CUR)/2;
        } else {
            count=fseek(f->nativehandle, len*2, SEEK_CUR)/2;
        }
        break;
    }
    return count;
}



static int32
JS_FileWrite(JSContext *cx, JSFile* f, jschar*buf, int32 len, int32 mode) 
{
    unsigned char*aux;
    int32 count,i,j;
    unsigned char*utfbuf;
    switch (mode) {
    case ASCII:
        aux = (unsigned char*)JS_malloc(cx, len);
        if (!aux) {
            JS_ReportOutOfMemory(cx);
            return 0;
        }
        for (i=0; i<len; i++) {
            aux[i]=buf[i]%256;
        }
        if (f->handle) {
            count = PR_Write(f->handle,aux,len);
        } else {
            count = fwrite(aux, 1, len, f->nativehandle);
        }
        if (count==-1) {
            JS_free(cx, aux);
            return 0;
        }
        JS_free(cx, aux);
        break;
    case UTF8:
        utfbuf = (unsigned char*)JS_malloc(cx, len*3);
        if (!utfbuf) { 
            JS_ReportOutOfMemory(cx);
            return 0;
        }
        i=0;
        for (count=0;count<len;count++) {
            j=one_ucs2_to_utf8_char(utfbuf+i,utfbuf+len*3,buf[count]);
            if (j==-1) {
                JS_free(cx, utfbuf);
                return 0;
            }
            i+=j;
        }
        if (f->handle) {
            j = PR_Write(f->handle, utfbuf, i);
        } else {
            j = fwrite(utfbuf, 1, i, f->nativehandle);
        }
        if (j<i) {
            JS_free(cx, utfbuf);
            return 0;
        }
        JS_free(cx, utfbuf);
      break;
    case UCS2:        
        if (f->handle) {
            count = PR_Write(f->handle,buf,len*2)>>1;
        } else {
            count = fwrite(buf, 1, len*2, f->nativehandle)>>1;
        }
        if (count==-1) {
            return 0;
        }
        break;
    }
    return count;
}



static JSBool
file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile *file;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,file->path));
    return JS_TRUE;
}

/* Ripped off from lm_win.c .. */
/* where is strcasecmp?.. for now, it's case sensitive.. */
static int32
file_has_option(char *options, char *name)
{
    char *comma, *equal;
    int32 found = 0;

    for (;;) {
        comma = strchr(options, ',');
        if (comma) *comma = '\0';
        equal = strchr(options, '=');
        if (equal) *equal = '\0';
        if (strcmp(options, name) == 0) {
            if (!equal || strcmp(equal + 1, "yes") == 0)
                found = 1;
            else
                found = atoi(equal + 1);
        }
        if (equal) *equal = '=';
        if (comma) *comma = ',';
        if (found || !comma)
            break;
        options = comma + 1;
    }
    return found;
}



static JSBool
file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile  *file;
    JSString *strmode, *strtype;
    char    *ctype;
    char    *mode;
    int32       mask;
    int32       type;
    PRFileDesc *handle;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (file->opened) {
        file_close(cx,obj,0,NULL, rval);
    }

    if (!file->path)
    return JS_FALSE;

    if (argc>=2) {
      strmode =JS_ValueToString(cx, argv[1]);
      if (!strmode ) {
        JS_ReportError(cx, FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR );
        return JS_FALSE;
      }
      mode = JS_strdup(cx,JS_GetStringBytes(strmode));
    } else
      mode = JS_strdup(cx,"readWrite,append,create"); /* non-destructive, permissive defaults. */
    if (argc>=1) {
      strtype = JS_ValueToString(cx, argv[0]);
      if (!strtype) {
        JS_ReportError(cx, SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR );
        return JS_FALSE;
      }
      ctype = JS_GetStringBytes(strtype);
    } else
      ctype = "";

    if (!strcmp(ctype,asciistring))
        type=ASCII;
    else
    if (!strcmp(ctype,utfstring))
        type=UTF8;
    else
    if (!strcmp(ctype,unicodestring))
        type=UCS2;
    else
        type=UTF8;


    mask=0;
    mask|=(file_has_option(mode,"readOnly"))?PR_RDONLY:0;
    mask|=(file_has_option(mode,"writeOnly"))?PR_WRONLY:0;
    mask|=(file_has_option(mode,"readWrite"))?PR_RDWR:0;
    mask|=(file_has_option(mode,"append"))?PR_APPEND:0;
    mask|=(file_has_option(mode,"create"))?PR_CREATE_FILE:0;
    mask|=(file_has_option(mode,"truncate"))?PR_TRUNCATE:0;
    mask|=(file_has_option(mode,"replace"))?PR_TRUNCATE:0;

    file->autoflush|=(file_has_option(mode,"autoflush"));

    JS_free(cx,mode);

    if ((mask&(PR_RDONLY|PR_WRONLY))==0)
        mask|=PR_RDWR;

    handle=PR_Open(file->path, mask, 0644); /* what about the permissions?? Java seems to ignore the problem.. */

    file->type=type;
    file->mode=mask;
    file->handle=handle;
    file->nativehandle=NULL;
    file->randomAccess=(type!=UTF8);


    resetBuffers(file);

    if (handle==NULL) {
        *rval=BOOLEAN_TO_JSVAL(JS_FALSE);
    } else {
        file->opened=JS_TRUE;
        *rval=BOOLEAN_TO_JSVAL(JS_TRUE);
    }

    return JS_TRUE;
}

static JSBool
file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile  *file;
    JSStatus status;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (file->handle==NULL) {
        *rval=JSVAL_FALSE;
        return JS_TRUE;
    }

    if (file->handle) {
        status=PR_Close(file->handle);
    } else {
        status=fclose(file->nativehandle);
    }
    if (status!=0) {
      *rval=JSVAL_FALSE;
      return JS_TRUE;
    }
    file->handle=NULL;
    file->nativehandle=NULL;
    file->opened=JS_FALSE;
    resetBuffers(file);
    *rval=JSVAL_TRUE;

    return JS_TRUE;
}


static JSBool
file_exists(JSFile*file)
{
    JSStatus status;
    status = PR_Access(file->path, PR_ACCESS_EXISTS);
    return (status==JS_SUCCESS);
}

static JSBool
file_canRead(JSFile*file)
{
    JSStatus status;
    status = PR_Access(file->path, PR_ACCESS_READ_OK);
    return (status==JS_SUCCESS);
}

static JSBool
file_canWrite(JSFile*file)
{
    JSStatus status;
    status = PR_Access(file->path, PR_ACCESS_WRITE_OK);
    return (status==JS_SUCCESS);
}

static JSBool
file_isFile(JSFile*file)
{
    
    JSStatus status;
    PRFileInfo info;
    if (file->opened&&file->handle) {
        status=PR_GetOpenFileInfo(file->handle,&info);
    } else {
        status=PR_GetFileInfo(file->path,&info);
    }
    if (status==JS_FAILURE)
        return JS_FALSE;
    return (info.type==PR_FILE_FILE);
}


static JSBool
file_isDirectory(JSFile*file)
{
    JSStatus status;
    PRFileInfo info;
    if (file->opened&&file->handle) {
      status=PR_GetOpenFileInfo(file->handle,&info);
    } else {
          status=PR_GetFileInfo(file->path,&info);
    }
    if (status==JS_FAILURE)
        return JS_FALSE;
    return (info.type==PR_FILE_DIRECTORY);
}

static JSBool
file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile  *file;
    JSStatus status;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (file_isDirectory(file)) {
      status = PR_RmDir(file->path);
    } else {
      status = PR_Delete(file->path);
    }
    if (status==JS_SUCCESS) {
        file->opened=JS_FALSE; /* I'm not sure I should do that.. */
        *rval = JSVAL_TRUE;
    } else {
        *rval = JSVAL_FALSE;
    }
    return JS_TRUE;
}

/* Raw PR-based function. no text processing. just raw data copying. */
static JSBool
file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile  *file;
    JSStatus status;
    char*str;
    PRFileDesc* handle;
    PRFileInfo info;
    char* buffer;
    uint32 count;

    if (argc!=1)
    return JS_FALSE; 

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    str=JS_GetStringBytes(JS_ValueToString(cx, argv[0]));

    if (file->opened) {
        JS_ReportError(cx, CANNOT_COPY_OPENED_FILE_ERROR);
        *rval = JSVAL_FALSE;
        return JS_TRUE;
    }

    handle=PR_Open(str, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644 );
    file->handle=PR_Open(file->path, PR_RDONLY, 0644);

    status=PR_GetOpenFileInfo(file->handle,&info);
    if (status==JS_FAILURE) {
      JS_ReportError(cx, CANNOT_ACCESS_FILE_INFO_ERROR );
      *rval = JSVAL_FALSE;
      return JS_TRUE;
    }

    buffer=JS_malloc(cx,info.size);
    count=PR_Read(file->handle,buffer,info.size);
    if (count!=info.size) {
        JS_free(cx,buffer);
        PR_Close(file->handle);
        PR_Close(handle);
        JS_ReportError(cx, COPY_READ_ERROR );
        *rval = JSVAL_FALSE;
        return JS_TRUE;
    }
    count=PR_Write(handle,buffer,info.size);
    if (count!=info.size) {
        JS_free(cx,buffer);
        PR_Close(file->handle);
        PR_Close(handle);
        JS_ReportError(cx, COPY_WRITE_ERROR );
        *rval=JSVAL_FALSE;
        return JS_TRUE;
    }
    JS_free(cx,buffer);
    PR_Close(file->handle);
    PR_Close(handle);

    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

static JSBool
file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile  *file;
    JSStatus status;
    char*str;

    if (argc<1) {
      JS_ReportError(cx, RENAME_EXPECTS_ONE_ARG_ERROR);
      return JS_FALSE; 
    }

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    str=JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
    status = PR_Rename(file->path,str);
    if (status==JS_FAILURE)
      *rval = JSVAL_FALSE;
    else 
      *rval = JSVAL_TRUE;

    return JS_TRUE;
    
}

static JSBool
file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile  *file;
    JSStatus status;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (!file->opened) {
        JS_ReportError(cx,CANNOT_FLUSH_CLOSE_FILE_ERROR);
        return JS_FALSE;
    }
    if (file->handle) {
        status = PR_Sync(file->handle);
    } else {
        status = fflush(file->nativehandle);
    }
    if (status==JS_FAILURE)
      *rval = JSVAL_FALSE;
    else
      *rval = JSVAL_TRUE;

    return JS_TRUE;
}


/* write(s) methods..
    First, check if the file is open.
    If not, try to open it with default values.
      ( here, append mode.. )
*/

static JSBool
file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN i;
    JSFile  *file;
    JSString *str;
    int32 count;

    *rval = BOOLEAN_TO_JSVAL(JS_FALSE);
    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (!file->opened) {
        JSString *type,*mask;
        jsval v[2];
        jsval rval;
        JSBool b;
        type= JS_NewStringCopyZ(cx, utfstring);
        mask= JS_NewStringCopyZ(cx, "create,append,writeOnly");
        v[0]=STRING_TO_JSVAL(type);
        v[1]=STRING_TO_JSVAL(mask);
        b=file_open(cx,obj,2,v,&rval);
        if (!file->opened) {
            JS_ReportError(cx, CANNOT_OPEN_WRITING_ERROR);
            return JS_FALSE;
        }
    }
    
    for (i=0;i<argc;i++) {
        str=JS_ValueToString(cx, argv[i]);
        count=JS_FileWrite(cx, file,JS_GetStringChars(str),JS_GetStringLength(str),file->type);
        if (count==-1) 
          return JS_TRUE;
    }

    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

static JSBool
file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile      *file;
    JSString    *str;
    JSBool      b;
    jsint         count;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    b=file_write(cx,obj,argc,argv,rval);
    if (!b)
    return JS_FALSE;

    str=JS_NewStringCopyZ(cx,LINEBREAK);
    count=JS_FileWrite(cx,file,JS_GetStringChars(str),JS_GetStringLength(str),file->type);

    if (file->autoflush)
        file_flush(cx,obj,0,NULL,NULL);

    *rval= BOOLEAN_TO_JSVAL(count>=0);
    return JS_TRUE;
}

static JSBool
file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile      *file;
    jsuint       iter;
    jsuint       limit;
    JSObject    *array;
    JSObject    *elem;
    jsval       elemval;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (argc<1) {
        JS_ReportError(cx, WRITEALL_EXPECTS_ONE_ARG_ERROR);
        return JS_FALSE;
    }

    if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
        JS_ReportError(cx, FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
        return JS_FALSE;
    }
    array=JSVAL_TO_OBJECT(argv[0]);
    if (!JS_GetArrayLength(cx, array, &limit)) {
        return JS_FALSE; /* not supposed to happen */
    }
    

    for (iter=0; iter<limit; iter++) {
      if (!JS_GetElement(cx, array, iter, &elemval))
          return JS_FALSE;
      elem=JSVAL_TO_OBJECT(elemval);
      file_writeln(cx,obj,1,&elemval,rval);
    }

    *rval = JSVAL_TRUE;
    return JS_TRUE;
}

static JSBool
file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile      *file;
    JSString    *str;
    int32       want,count;
    jschar      *buf;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (!file->opened) {
        JSString *type,*mask;
        jsval v[2];
        jsval rval;
        JSBool b;
        type= JS_NewStringCopyZ(cx, utfstring);
        mask= JS_NewStringCopyZ(cx, "readOnly");
        v[0]=STRING_TO_JSVAL(type);
        v[1]=STRING_TO_JSVAL(mask);
        b=file_open(cx,obj,2,v,&rval);
        if (!file->opened) {
            JS_ReportError(cx, CANNOT_OPEN_FILE_ERROR);
            return JS_FALSE;
        }
    }
    
    if (!JS_ValueToInt32(cx, argv[0], &want))
    return JS_FALSE;

    /* want=(want>262144)?262144:want; * arbitrary size limitation */
                                    
    buf = JS_malloc(cx,want*sizeof buf[0]); 
    if (!buf)
    return JS_FALSE;

    count= JS_FileRead(cx, file,buf,want,file->type);
    if (count>0) {
      str = JS_NewUCStringCopyN(cx, buf, count);
      *rval=STRING_TO_JSVAL(str);
      JS_free(cx,buf);
    } else {
      JS_free(cx,buf);
      *rval=JSVAL_NULL;
      return JS_TRUE;
    }

    return JS_TRUE;
}


static JSBool
file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile      *file;
    JSString    *str;
    jschar      *buf;
    int32       offset;
    intN        room;
    jschar      data,data2;
    JSBool  endofline;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (!file->opened) {
        JSString *type,*mask;
        jsval v[2];
        jsval rval;
        JSBool b;
        type= JS_NewStringCopyZ(cx, utfstring);
        mask= JS_NewStringCopyZ(cx, "readOnly");
        v[0]=STRING_TO_JSVAL(type);
        v[1]=STRING_TO_JSVAL(mask);
        b=file_open(cx,obj,2,v,&rval);
        if (!file->opened) {
            JS_ReportError(cx, CANNOT_OPEN_FILE_ERROR );
            return JS_FALSE;
        }
    }

    if (!file->linebuffer) {
        buf = JS_malloc(cx, 128*(sizeof data));
        if (!buf) {
            JS_ReportOutOfMemory(cx);
            return JS_FALSE;
        }           
        file->linebuffer=JS_NewUCString(cx,buf,128);
    }
    room=JS_GetStringLength(file->linebuffer);
    offset=0;

    /* XXX TEST ME!! */
    for(;;) {
        if (!JS_FileRead(cx, file,&data,1,file->type)) {
            endofline=JS_FALSE;
            goto loop;
        }
        switch (data) {
        case '\n' : 
            endofline=JS_TRUE;
            goto loop;
        case '\r' :
            if (!JS_FileRead(cx, file,&data2,1,file->type)) {
                endofline=JS_TRUE;
                goto loop;
            }
            if (data2!='\n') { /* we read one char to far. buffer it. */
                file->charBuffer=data2;
                file->charBufferUsed=JS_TRUE;
            }
            endofline=JS_TRUE;
            goto loop;
        default:
            if (--room < 0) {
                buf = JS_malloc(cx, (offset+ 128)*sizeof data);
                if (!buf) {
                    JS_ReportOutOfMemory(cx);
                    return JS_FALSE;
                }
                room = 127;
                memcpy(buf,JS_GetStringChars(file->linebuffer),
                    JS_GetStringLength(file->linebuffer));
                /* what follows may not be the cleanest way. */
                file->linebuffer->chars = buf;
                file->linebuffer->length= offset+128;
            }
            file->linebuffer->chars[offset++] = data;
            break;
        }
    }
loop:
    file->linebuffer->chars[offset]=0;
    if ((endofline==JS_TRUE)) {
    str = JS_NewUCStringCopyN(cx,JS_GetStringChars(file->linebuffer),
          offset);
    *rval=STRING_TO_JSVAL(str);
    } else
    *rval = JSVAL_NULL;
    return JS_TRUE;
}

static JSBool
file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile      *file;
    PRFileInfo  info;
    JSStatus    status;
    JSObject    *array;
    jsint         len;
    jsval       line;
    JSBool      ok=JS_TRUE;


    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    array=JS_NewArrayObject(cx, 0, NULL);
    *rval = OBJECT_TO_JSVAL(array);
    len = 0;


    if (!file->opened) {
        JSString *type,*mask;
        jsval v[2];
        jsval rval;
        JSBool b;
        type= JS_NewStringCopyZ(cx, utfstring);
        mask= JS_NewStringCopyZ(cx, "readOnly");
        v[0]=STRING_TO_JSVAL(type);
        v[1]=STRING_TO_JSVAL(mask);
        b=file_open(cx,obj,2,v,&rval);
        if (!file->opened) {
            JS_ReportError(cx, CANNOT_OPEN_FILE_ERROR);
            return JS_FALSE;
        }
    }

    if (file->handle) {
        status=PR_GetOpenFileInfo(file->handle,&info);
    } else {
        status=PR_GetFileInfo(file->path,&info);
    }

    if (status==JS_FAILURE)
    return JS_FALSE;
    
    if (file->handle) {
        while (ok&&(info.size>(JSUint32)PR_Seek(file->handle, 0, PR_SEEK_CUR))) {
            ok=file_readln(cx,obj,0,NULL,&line);
            JS_SetElement(cx, array, len, &line);
            len++;
        }
    } else {
        while (ok&&(info.size>(JSUint32)fseek(file->nativehandle, 0, SEEK_CUR))) {
            ok=file_readln(cx,obj,0,NULL,&line);
            JS_SetElement(cx, array, len, &line);
            len++;
        }        
    }

    return JS_TRUE;
}

static JSBool
file_skip(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFile      *file;
    int32       toskip,count;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (!file->opened) {
        JSString *type,*mask;
        jsval v[2];
        jsval rval;
        JSBool b;
        type= JS_NewStringCopyZ(cx, utfstring);
        mask= JS_NewStringCopyZ(cx, "readOnly");
        v[0]=STRING_TO_JSVAL(type);
        v[1]=STRING_TO_JSVAL(mask);
        b=file_open(cx,obj,2,v,&rval);
        if (!file->opened) {
            JS_ReportError(cx, CANNOT_OPEN_FILE_ERROR);
            return JS_FALSE;
        }
    }
    
    if (!JS_ValueToInt32(cx, argv[0], &toskip))
    return JS_FALSE;

    count= JS_FileSkip(file,toskip,file->type);
    if (count!=toskip)
        return JS_FALSE;

    return JS_TRUE;
}

static JSBool
file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 
{
    PRDir *dir;
    PRDirEntry *entry;
    JSFile * file;
    JSObject *array;
    JSObject *each;
    JSFile *eachObj;
    jsint len;
    jsval v;
    JSRegExp *re = NULL;
    JSFunction *func = NULL;
    JSString * tmp;
    size_t index;
    jsval args[1];
    char* aux;


    if (argc>0) {
        if (JSVAL_IS_REGEXP(cx,argv[0])) {
          re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
        } else
        if (JSVAL_IS_FUNCTION(cx,argv[0])) {
          func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
        }
    }

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    if (!file_isDirectory(file)) {
        *rval = JSVAL_FALSE;
        return JS_TRUE; /* or return an empty array? or do a readAll?  */
    }
    dir=PR_OpenDir(file->path);
    /* Create JSArray here.. */
    array=JS_NewArrayObject(cx, 0, NULL);
    *rval = OBJECT_TO_JSVAL(array);
    len=0;

    entry=NULL;
    if (dir==NULL)
    return JS_TRUE;
    
    while ((entry=PR_ReadDir(dir,PR_SKIP_BOTH))!=NULL) {
        /* First, check if we have a filter */
        if (re!=NULL) {
            tmp=JS_NewStringCopyZ(cx,entry->name);
            index=0;
            js_ExecuteRegExp(cx, re, tmp, &index, JS_TRUE, &v);
            if (v==JSVAL_NULL) {
                continue;
            }

        }
        if (func!=NULL) {
            tmp=JS_NewStringCopyZ(cx,entry->name);
            args[0]=STRING_TO_JSVAL(tmp);
            JS_CallFunction(cx, obj, func, 1, args, &v);
            if (v==JSVAL_FALSE) {
                continue;
            }
        }
        aux = combinePath(cx, file->path, (char*)entry->name);

        each=NewFileObject(cx, aux);
        JS_free(cx, aux);
        if (!each)
        return JS_FALSE;
        eachObj = JS_GetInstancePrivate(cx, each, &file_class, NULL);
        if (!eachObj)
        return JS_FALSE;
        v=OBJECT_TO_JSVAL(each);
        JS_SetElement(cx, array, len, &v);
        JS_SetProperty(cx, array, entry->name, &v); /* accessible by name.. make sense I think.. */
        len++;
    }
    PR_CloseDir(dir);

    return JS_TRUE;
}

static JSBool
file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 
{
    JSFile * file;
    JSStatus status;
    char * str;
    JSObject*newobj;


    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_FALSE;

    str = fileDirectoryName(cx, file->path);
    if (strcmp(str,file->path)) {
        newobj=NewFileObject(cx, str);
        if (!file_mkdir(cx, newobj, argc, argv, rval)) {
            return JS_FALSE;
        }
    }
    JS_free(cx, str);

    status=PR_MkDir(file->path, 0755);
    if (status==JS_FAILURE) 
        *rval=JSVAL_FALSE;
    else
        *rval=JSVAL_TRUE;
    return JS_TRUE;
}

static JSFunctionSpec file_functions[] = {
    { "toString",       file_toString, 0},
    { "open",           file_open, 0},
    { "close",          file_close, 0},
    { "remove",         file_remove, 0},
    { "copyTo",         file_copyTo, 0},
    { "renameTo",       file_renameTo, 0},
    { "flush",          file_flush, 0},
    { "skip",           file_skip, 0},
    { "read",           file_read, 0},
    { "readln",         file_readln, 0},
    { "readAll",        file_readAll, 0},
    { "write",          file_write, 0},
    { "writeln",        file_writeln, 0},
    { "writeAll",       file_writeAll, 0},
    { "list",           file_list, 0},
    { "mkdir",          file_mkdir, 0},
    {0}
};

enum file_tinyid {
    FILE_LENGTH             = -2,
    FILE_PARENT             = -3,
    FILE_PATH               = -4,
    FILE_NAME               = -5,
    FILE_ISDIR              = -6,
    FILE_ISFILE             = -7,
    FILE_EXISTS             = -8,
    FILE_CANREAD            = -9,
    FILE_CANWRITE           = -10,
    FILE_OPENED             = -11,
    FILE_TYPE               = -12,
    FILE_MODE               = -13,
    FILE_CREATED            = -14,
    FILE_MODIFIED           = -15,
    FILE_SIZE               = -16,
    FILE_RANDOMACCESS       = -17,
    FILE_POSITION           = -18

};

static JSPropertySpec file_props[] = {
    {"length",      FILE_LENGTH,    JSPROP_ENUMERATE | JSPROP_READONLY }, 
    {"parent",      FILE_PARENT,    JSPROP_ENUMERATE | JSPROP_READONLY },
    {"path",        FILE_PATH,      JSPROP_ENUMERATE | JSPROP_READONLY },
    {"name",        FILE_NAME,      JSPROP_ENUMERATE | JSPROP_READONLY },
    {"isDirectory", FILE_ISDIR,     JSPROP_ENUMERATE | JSPROP_READONLY },
    {"isFile",      FILE_ISFILE,    JSPROP_ENUMERATE | JSPROP_READONLY },
    {"exists",      FILE_EXISTS,    JSPROP_ENUMERATE | JSPROP_READONLY },
    {"canRead",     FILE_CANREAD,   JSPROP_ENUMERATE | JSPROP_READONLY },
    {"canWrite",    FILE_CANWRITE,  JSPROP_ENUMERATE | JSPROP_READONLY },
    {"opened",      FILE_OPENED,    JSPROP_ENUMERATE | JSPROP_READONLY },
    {"type",        FILE_TYPE,      JSPROP_ENUMERATE | JSPROP_READONLY },
    {"mode",        FILE_MODE,      JSPROP_ENUMERATE | JSPROP_READONLY },
    {"creationTime",FILE_CREATED,   JSPROP_ENUMERATE | JSPROP_READONLY },
    {"lastModified",FILE_MODIFIED,  JSPROP_ENUMERATE | JSPROP_READONLY },
    {"size",        FILE_SIZE,      JSPROP_ENUMERATE | JSPROP_READONLY },
    {"randomAccess",FILE_RANDOMACCESS,  JSPROP_ENUMERATE | JSPROP_READONLY },
    {"position",    FILE_POSITION,  JSPROP_ENUMERATE }, 
    {0}
};


static JSBool JS_DLL_CALLBACK
file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFile * file;
    char *str;
    jsint tiny;
    JSStatus status;
    PRFileInfo info;
    PRExplodedTime  expandedTime;
    JSObject * tmp;
/*     int aux,index; */
    JSObject * newobj;
    JSString * newstring;
    JSBool flag;

    tiny=JSVAL_TO_INT(id);
    file= JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
    return JS_TRUE;

    switch (tiny) {
    case FILE_PARENT:
        str=fileDirectoryName(cx, file->path);
        newobj=NewFileObject(cx, str);
        JS_free(cx,str);
        *vp = OBJECT_TO_JSVAL(newobj);
        break;
    case FILE_PATH:
        *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,file->path));
        break;
    case FILE_NAME:
        str=fileBaseName(cx, file->path);
        newstring=JS_NewString(cx, str,strlen(str));
        *vp = STRING_TO_JSVAL(newstring);
        break;
    case FILE_ISDIR:
        *vp = BOOLEAN_TO_JSVAL(file_isDirectory(file));
        break;
    case FILE_ISFILE:
        *vp = BOOLEAN_TO_JSVAL(file_isFile(file));
        break;
    case FILE_EXISTS:
        *vp = BOOLEAN_TO_JSVAL(file_exists(file));
        break;
    case FILE_CANREAD:
        *vp = BOOLEAN_TO_JSVAL(file_canRead(file));
        break;
    case FILE_CANWRITE:
        *vp = BOOLEAN_TO_JSVAL(file_canWrite(file));
        break;
    case FILE_OPENED:
        *vp = BOOLEAN_TO_JSVAL(file->opened);
        break;
    case FILE_TYPE:
        switch (file->type) {
        case ASCII: 
            *vp = STRING_TO_JSVAL(JS_NewString(cx,asciistring,strlen(asciistring)));
            break;
        case UTF8:
            *vp = STRING_TO_JSVAL(JS_NewString(cx,utfstring,strlen(utfstring)));
            break;
        case UCS2:
            *vp = STRING_TO_JSVAL(JS_NewString(cx,unicodestring,strlen(unicodestring)));
            break;
        default:
            ; /* time to panic, or to do nothing.. */
        }
        break;
    case FILE_MODE:
        str=(char*)JS_malloc(cx,200); /* Big enough to contain all the modes. */
        str[0]='\0';
        flag=JS_FALSE;

        if ((file->mode&PR_RDONLY)==PR_RDONLY) {
            if (flag) strcat(str,",");
            strcat(str,"readOnly");
            flag=JS_TRUE;
        }
        if ((file->mode&PR_WRONLY)==PR_WRONLY) {
            if (flag) strcat(str,",");
            strcat(str,"writeOnly");
            flag=JS_TRUE;
        }
        if ((file->mode&PR_RDWR)==PR_RDWR) {
            if (flag) strcat(str,",");
            strcat(str,"readWrite");
            flag=JS_TRUE;
        }
        if ((file->mode&PR_APPEND)==PR_APPEND) {
            if (flag) strcat(str,",");
            strcat(str,"append");
            flag=JS_TRUE;
        }
        if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {
            if (flag) strcat(str,",");
            strcat(str,"create");
            flag=JS_TRUE;
        }
        if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {
            if (flag) strcat(str,",");
            strcat(str,"replace");
            flag=JS_TRUE;
        }
        if (file->autoflush) {
            if (flag) strcat(str,",");
            strcat(str,"autoflush");
            flag=JS_TRUE;
        }
        newstring=JS_NewStringCopyZ(cx, str);
        *vp = STRING_TO_JSVAL(newstring);
        JS_free(cx, str);
        break;
    case FILE_CREATED:
        if (file->opened&&file->handle) {
          status=PR_GetOpenFileInfo(file->handle,&info);
        } else {
          status=PR_GetFileInfo(file->path,&info);
        }
        if (status==JS_FAILURE)
          break;
        
        PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters, &expandedTime);
        tmp=js_NewDateObject(cx,    expandedTime.tm_year, 
                                    expandedTime.tm_month,
                                    expandedTime.tm_mday,
                                    expandedTime.tm_hour,
                                    expandedTime.tm_min,
                                    expandedTime.tm_sec );
        *vp = OBJECT_TO_JSVAL(tmp);
        break;
    case FILE_MODIFIED:
        if (file->opened&&file->handle) {
          status=PR_GetOpenFileInfo(file->handle,&info);
        } else {
          status=PR_GetFileInfo(file->path,&info);
        }
        if (status==JS_FAILURE)
          break;
        
        PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);
        tmp=js_NewDateObject(cx,    expandedTime.tm_year, 
                                    expandedTime.tm_month,
                                    expandedTime.tm_mday,
                                    expandedTime.tm_hour,
                                    expandedTime.tm_min,
                                    expandedTime.tm_sec );
        *vp = OBJECT_TO_JSVAL(tmp);
        break;
    case FILE_LENGTH:
        if (file_isDirectory(file)) { /* XXX debug me */
          PRDir *dir;
          PRDirEntry *entry;
          jsint count;

          dir=PR_OpenDir(file->path);
          if (dir!=NULL)
            entry=PR_ReadDir(dir,PR_SKIP_BOTH);
          else
            break;

          count=0;
          while (entry!=NULL) {
            count++;
            entry=PR_ReadDir(dir,PR_SKIP_BOTH);
          }
          PR_CloseDir(dir);
          *vp = INT_TO_JSVAL(count);
          break;
        }

        if (file->opened&&file->handle) {
            status=PR_GetOpenFileInfo(file->handle,&info);
        } else {
            status=PR_GetFileInfo(file->path,&info);
        }
        if (status!=JS_FAILURE)     
          *vp = INT_TO_JSVAL(info.size);
        break;
    case FILE_RANDOMACCESS:
        *vp = BOOLEAN_TO_JSVAL(file->randomAccess);
        break;
    case FILE_POSITION:
        if (file->opened) {
            if (file->handle) {
                *vp = INT_TO_JSVAL(PR_Seek(file->handle, 0, PR_SEEK_CUR));
            } else {
                *vp = INT_TO_JSVAL(fseek(file->nativehandle, 0, SEEK_CUR));
            }
        } else {
          *vp = JSVAL_VOID;
        }
        break;
    default:
        break;
    }
    return JS_TRUE;
}

static JSBool JS_DLL_CALLBACK
file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFile *file;
    jsint slot;
    int32 offset;
    int32 count;

    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file) {
        return JS_TRUE;
    }

    if (JSVAL_IS_STRING(id)) {
        return JS_TRUE;
    }
    
    slot = JSVAL_TO_INT(id);

    switch (slot) {
    case FILE_POSITION:
        if (file->randomAccess) {
            offset=JSVAL_TO_INT(*vp);
            if (file->handle) {
                count = PR_Seek( file->handle, offset, PR_SEEK_SET);
            } else {
                count = fseek( file->nativehandle, offset, SEEK_SET);
            }
            resetBuffers(file);
            *vp = INT_TO_JSVAL(count);
        }
        break;
    default:
        break;
    }

    return JS_TRUE;
}

static void JS_DLL_CALLBACK
file_finalize(JSContext *cx, JSObject *obj)
{
    JSFile *file;
    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
        return;
    JS_free(cx, file->path);
    if (file->linebuffer)
        JS_free(cx, file->linebuffer);
    JS_free(cx, file);
}


static JSClass file_class = {
    FILE_CONSTRUCTOR, JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  file_getProperty,  file_setProperty,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   file_finalize
};

static JSFile*
file_constructor(JSContext *cx, JSObject*obj, char *bytes)
{
    JSFile      *file;

    file=JS_malloc(cx, sizeof *file);
    if (!file)
        return NULL;
    memset(file, 0 , sizeof *file);

    if (strcmp(bytes, SPECIAL_FILE_STRING))
        file->path=canonicalPath(cx, bytes);
    else
        file->path=JS_strdup(cx,SPECIAL_FILE_STRING);

    file->opened=JS_FALSE;
    file->handle=NULL;
    file->nbBytesInBuf=0;
    file->charBufferUsed=JS_FALSE;
    file->randomAccess=JS_TRUE; /* innocent until proven guilty */

    if (!JS_SetPrivate(cx, obj, file)) {
        JS_free(cx, file);
        return NULL;
    }

    return file;
}

JS_PUBLIC_API(JSObject*)
NewFileObject(JSContext *cx, char*bytes)
{
    JSObject    *f;
    JSFile      *file;
    f=JS_NewObject(cx, &file_class, NULL, NULL);
    if (!f)
    return NULL;

    file=file_constructor(cx,f,bytes);
    if (!file)
    return NULL;

    return f;
}

JS_PUBLIC_API(JSObject*)
NewFileObjectFromFILE(JSContext *cx, FILE*f)
{
    JSObject *obj;
    JSFile *file;
    obj = NewFileObject(cx,SPECIAL_FILE_STRING);
    file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
    if (!file)
        return NULL;
    file->nativehandle=f;
    file->handle=NULL;
    file->opened=JS_TRUE;
    return obj;
}

static JSBool
File(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 
{

    JSString *str;
    JSFile   *file;


    if (argc==0) {
        str=JS_InternString(cx,"");
    } else
        str=JS_ValueToString(cx,argv[0]);
    if (!str) {
        JS_ReportError(cx, FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR);
        return JS_FALSE;
    }

    file = file_constructor(cx,obj,JS_GetStringBytes(str));
    if (!file) {
        *rval=JSVAL_VOID;
        return JS_TRUE;
    }

    return JS_TRUE;
}

static JSBool JS_DLL_CALLBACK
file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSObject *object, *ctor;
    JSString *str;
    char*path;
    JSFile *fileObj;


    if (JSVAL_IS_OBJECT(*vp)) {
        object=JSVAL_TO_OBJECT(*vp);
        ctor = JS_GetConstructor(cx, object);
        if (ctor&&(ctor==obj)) {
            fileObj = JS_GetInstancePrivate(cx, object, &file_class, NULL);
            if (fileObj&&(!file_exists(fileObj)||!file_isDirectory(fileObj)))
                JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
            return JS_TRUE;
        }
    }
    str = JS_ValueToString(cx, *vp);
    path= JS_GetStringBytes(str);
    object = NewFileObject(cx, path);
    chdir(path);
    if (!object)
        return JS_FALSE;
    *vp=OBJECT_TO_JSVAL(object);
    fileObj = JS_GetInstancePrivate(cx, object, &file_class, NULL);
    if (!file_exists(fileObj)||!file_isDirectory(fileObj))
        JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
    return JS_TRUE;
}


JSObject* 
js_InitFileClass(JSContext *cx, JSObject* obj) 
{
    JSObject * file, *ctor;
    JSObject    *afile;
/*     JSFile      *fileObj; */
    jsval       vp;
    char* currentdir;
    
    file = JS_InitClass(cx, obj, NULL, &file_class, File, 1,
        file_props, file_functions, NULL, NULL);

    if (!file)
        return NULL;

    ctor = JS_GetConstructor(cx, file);
    if (!ctor) {
        return NULL;
    }

    /*
    afile = NewFileObject(cx, CURRENT_DIR);
    */
    currentdir= JS_malloc(cx, MAX_PATH_LENGTH);
    currentdir= getcwd(currentdir, MAX_PATH_LENGTH );
    afile = NewFileObject(cx, currentdir );
    JS_free(cx, currentdir);
    
    vp = OBJECT_TO_JSVAL(afile);
    JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,
                JS_PropertyStub, file_currentDirSetter, JSPROP_ENUMERATE);


/* Code to create stdin, stdout, and stderr. commented out here. insert in the appropriate place.

    afile=NewFileObjectFromFILE(cx,stdin);
    if (!afile)
    return NULL;
    fileObj = JS_GetInstancePrivate(cx, afile, &file_class, NULL);
    if (!fileObj)
    return NULL;
    fileObj->randomAccess=JS_FALSE;
    vp=OBJECT_TO_JSVAL(afile);
    JS_SetProperty(cx, ctor, "stdin", &vp);
    JS_SetProperty(cx, obj, "stdin", &vp);

    afile=NewFileObjectFromFILE(cx,stdout);
    if (!afile)
    return NULL;
    fileObj = JS_GetInstancePrivate(cx, afile, &file_class, NULL);
    if (!fileObj)
    return NULL;
    fileObj->randomAccess=JS_FALSE;
    vp=OBJECT_TO_JSVAL(afile);
    JS_SetProperty(cx, ctor, "stdout", &vp);
    JS_SetProperty(cx, obj, "stdout", &vp);

    afile=NewFileObject(cx,stderr);
    if (!afile)
    return NULL;
    fileObj = JS_GetInstancePrivate(cx, afile, &file_class, NULL);
    if (!fileObj)
    return NULL;
    fileObj->randomAccess=JS_FALSE;
    vp=OBJECT_TO_JSVAL(afile);
    JS_SetProperty(cx, ctor, "stderr", &vp);
    JS_SetProperty(cx, obj, "stderr", &vp);
*/

    return file;
}
#endif /* JS_HAS_FILE_OBJECT */

**** End of jsfile.c. ****

**** Start of jsfile.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef _jsfile_h__
#define _jsfile_h__

extern JSObject*
js_InitFileClass(JSContext *cx, JSObject* obj);


#endif /* _jsfile_h__ */


**** End of jsfile.h. ****

**** Start of jsfun.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS function support.
 */
#include "jsstddef.h"
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

enum {
    CALL_ARGUMENTS  = -1,       /* predefined arguments local variable */
    ARGS_LENGTH     = -2,       /* number of actual args, arity if inactive */
    ARGS_CALLEE     = -3,       /* reference to active function's object */
    ARGS_CALLER     = -4,       /* arguments or call object that invoked us */
    FUN_ARITY       = -5,       /* number of formal parameters; desired argc */
    FUN_NAME        = -6,       /* function name, "" if anonymous */
    FUN_CALL        = -7        /* __call__ var, function's top Call object */
};

#undef TEST_BIT
#undef SET_BIT
#define TEST_BIT(tinyid, bitset)    ((bitset) & JS_BIT(-1 - (tinyid)))
#define SET_BIT(tinyid, bitset)     ((bitset) |= JS_BIT(-1 - (tinyid)))

#if JS_HAS_ARGS_OBJECT

JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
{
    JSObject *argsobj;

    /* Create an arguments object for fp only if it lacks one. */
    JS_ASSERT(fp->fun);
    argsobj = fp->argsobj;
    if (argsobj)
	return argsobj;

    /* Link the new object to fp so it can get actual argument values. */
    argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);
    if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {
	cx->newborn[GCX_OBJECT] = NULL;
	return NULL;
    }
    fp->argsobj = argsobj;
    return argsobj;
}

static JSBool
args_enumerate(JSContext *cx, JSObject *obj);

JSBool
js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
{
    JSObject *argsobj;
    JSBool ok;
    JSRuntime *rt;
    jsval rval;

    /*
     * Reuse args_enumerate here to reflect fp's actual arguments as indexed
     * elements of argsobj.
     */
    argsobj = fp->argsobj;
    if (!argsobj)
	return JS_TRUE;
    ok = args_enumerate(cx, argsobj);

    /*
     * Now get the prototype properties so we snapshot fp->fun, fp->down->fun,
     * and fp->argc before fp goes away.
     */
    rt = cx->runtime;
    ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval);
    ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval);
    ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.callerAtom, &rval);
    ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.callerAtom, &rval);
    ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval);
    ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval);

    /*
     * Clear the private pointer to fp, which is about to go away (js_Invoke).
     * Do this last because the args_enumerate and js_GetProperty calls above
     * need to follow the private slot to find fp.
     */
    ok &= JS_SetPrivate(cx, argsobj, NULL);
    fp->argsobj = NULL;
    return ok;
}

static JSBool
Arguments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(obj);
    }
    return JS_TRUE;
}

static JSPropertySpec args_props[] = {
    {js_length_str,     ARGS_LENGTH,    0},
    {js_callee_str,     ARGS_CALLEE,    0},
    {js_caller_str,     ARGS_CALLER,    0},
    {0}
};

static JSBool
args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint slot;
    JSStackFrame *fp;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);
    fp = JS_GetPrivate(cx, obj);

    switch (slot) {
      case ARGS_CALLEE:
	if (fp && !TEST_BIT(slot, fp->overrides))
	    *vp = OBJECT_TO_JSVAL(fp->fun->object);
	break;

      case ARGS_CALLER:
	if (fp && !TEST_BIT(slot, fp->overrides)) {
	    if (fp->down && fp->down->fun) {
		JSObject *argsobj = js_GetArgsObject(cx, fp->down);
		if (!argsobj)
		    return JS_FALSE;
		*vp = OBJECT_TO_JSVAL(argsobj);
	    } else {
		*vp = JSVAL_NULL;
	    }
	}
	break;

      case ARGS_LENGTH:
	if (fp && !TEST_BIT(slot, fp->overrides))
	    *vp = INT_TO_JSVAL((jsint)fp->argc);
	break;

      default:
	if (fp && (uintN)slot < fp->argc)
	    *vp = fp->argv[slot];
	break;
    }
    return JS_TRUE;
}

static JSBool
args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint slot;
    JSStackFrame *fp;
    jsdouble argc;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);
    fp = JS_GetPrivate(cx, obj);

    switch (slot) {
      case ARGS_CALLEE:
      case ARGS_CALLER:
	if (fp)
	    SET_BIT(slot, fp->overrides);
	break;

      case ARGS_LENGTH:
	if (fp) {
	    if (!js_ValueToNumber(cx, *vp, &argc))
		return JS_FALSE;
	    argc = js_DoubleToInteger(argc);
	    if (0 <= argc && argc < fp->argc)
		fp->argc = (uintN)argc;
	    SET_BIT(slot, fp->overrides);
	}
	break;

      default:
	if (fp && (uintN)slot < fp->argc)
	    fp->argv[slot] = *vp;
	break;
    }
    return JS_TRUE;
}

static JSBool
args_enumerate(JSContext *cx, JSObject *obj)
{
    JSStackFrame *fp;
    uintN attrs, slot;

    fp = JS_GetPrivate(cx, obj);
    if (!fp)
	return JS_TRUE;

    /* XXX ECMA specs DontEnum, contrary to all other array-like objects */
    attrs = JSVERSION_IS_ECMA(cx->version) ? 0 : JSPROP_ENUMERATE;

    for (slot = 0; slot < fp->argc; slot++) {
	if (!js_DefineProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot),
			       fp->argv[slot], NULL, NULL, attrs,
			       NULL)) {
	    return JS_FALSE;
	}
    }
    return JS_TRUE;
}

JSClass js_ArgumentsClass = {
    "Arguments",
    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
    JS_PropertyStub,  JS_PropertyStub,
    args_getProperty, args_setProperty,
    args_enumerate,   JS_ResolveStub,
    JS_ConvertStub,   JS_FinalizeStub
};

#endif /* JS_HAS_ARGS_OBJECT */

#if JS_HAS_CALL_OBJECT

JSObject *
js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent,
		 JSObject *withobj)
{
    JSObject *callobj, *funobj, *obj;

    /* Create a call object for fp only if it lacks one. */
    JS_ASSERT(fp->fun);
    callobj = fp->callobj;
    if (callobj)
	return callobj;

    /* The default call parent is its function's parent (static link). */
    funobj = fp->fun->object;
    if (!parent && funobj)
	parent = OBJ_GET_PARENT(cx, funobj);
    callobj = js_NewObject(cx, &js_CallClass, NULL, parent);
    if (!callobj || !JS_SetPrivate(cx, callobj, fp)) {
	cx->newborn[GCX_OBJECT] = NULL;
	return NULL;
    }
    fp->callobj = callobj;

    /* Splice callobj into the scope chain. */
    if (!withobj) {
	for (obj = fp->scopeChain; obj; obj = parent) {
	    if (OBJ_GET_CLASS(cx, obj) != &js_WithClass)
		break;
	    parent = OBJ_GET_PARENT(cx, obj);
	    if (parent == funobj) {
		withobj = obj;
		break;
	    }
	}
    }
    if (withobj)
	OBJ_SET_PARENT(cx, withobj, callobj);
    else
	fp->scopeChain = callobj;
    return callobj;
}

static JSBool
call_enumerate(JSContext *cx, JSObject *obj);

JSBool
js_PutCallObject(JSContext *cx, JSStackFrame *fp)
{
    JSObject *callobj;
    JSBool ok;
    jsid argsid;
    jsval aval;

    /*
     * Reuse call_enumerate here to reflect all actual args and vars into the
     * call object from fp.
     */
    callobj = fp->callobj;
    if (!callobj)
	return JS_TRUE;
    ok = call_enumerate(cx, callobj);

    /*
     * Get the arguments object to snapshot fp's actual argument values.
     */
    argsid = (jsid) cx->runtime->atomState.argumentsAtom;
    ok &= js_GetProperty(cx, callobj, argsid, &aval);
    ok &= js_SetProperty(cx, callobj, argsid, &aval);
    ok &= js_PutArgsObject(cx, fp);

    /*
     * Clear the private pointer to fp, which is about to go away (js_Invoke).
     * Do this last because the call_enumerate and js_GetProperty calls above
     * need to follow the private slot to find fp.
     */
    ok &= JS_SetPrivate(cx, callobj, NULL);
    fp->callobj = NULL;
    return ok;
}

static JSBool
Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_CallClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(obj);
    }
    return JS_TRUE;
}

static JSPropertySpec call_props[] = {
    {js_arguments_str,  CALL_ARGUMENTS, JSPROP_PERMANENT},
    {"__callee__",      ARGS_CALLEE,    0},
    {"__caller__",      ARGS_CALLER,    0},
    {"__call__",        FUN_CALL,       0},
    {0}
};

static JSBool
call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSStackFrame *fp;
    jsint slot;

    fp = JS_GetPrivate(cx, obj);
    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);

    switch (slot) {
      case CALL_ARGUMENTS:
	if (fp && !TEST_BIT(slot, fp->overrides)) {
	    JSObject *argsobj = js_GetArgsObject(cx, fp);
	    if (!argsobj)
		return JS_FALSE;
	    *vp = OBJECT_TO_JSVAL(argsobj);
	}
	break;

      case ARGS_CALLEE:
	if (fp && !TEST_BIT(slot, fp->overrides))
	    *vp = OBJECT_TO_JSVAL(fp->fun->object);
	break;

      case ARGS_CALLER:
	if (fp && !TEST_BIT(slot, fp->overrides)) {
	    if (fp->down && fp->down->fun) {
		JSObject *callobj = js_GetCallObject(cx, fp->down, NULL, NULL);
		if (!callobj)
		    return JS_FALSE;
		*vp = OBJECT_TO_JSVAL(callobj);
	    } else {
		*vp = JSVAL_NULL;
	    }
	}
	break;

      case FUN_CALL:
	*vp = OBJECT_TO_JSVAL(obj);
	break;

      default:
	if (fp && (uintN)slot < fp->argc)
	    *vp = fp->argv[slot];
	break;
    }
    return JS_TRUE;
}

static JSBool
call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSStackFrame *fp;
    jsint slot;

    fp = JS_GetPrivate(cx, obj);
    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);

    switch (slot) {
      case CALL_ARGUMENTS:
      case ARGS_CALLEE:
      case ARGS_CALLER:
      case FUN_CALL:
	if (fp)
	    SET_BIT(slot, fp->overrides);
	break;

      default:
	if (fp && (uintN)slot < fp->argc)
	    fp->argv[slot] = *vp;
	break;
    }
    return JS_TRUE;
}

JSBool
js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSStackFrame *fp;

    JS_ASSERT(JSVAL_IS_INT(id));
    fp = JS_GetPrivate(cx, obj);
    if (fp) {
	/* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */
	if ((uintN)JSVAL_TO_INT(id) < fp->nvars)
	    *vp = fp->vars[JSVAL_TO_INT(id)];
    }
    return JS_TRUE;
}

JSBool
js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSStackFrame *fp;

    JS_ASSERT(JSVAL_IS_INT(id));
    fp = JS_GetPrivate(cx, obj);
    if (fp) {
	/* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */
	jsint slot = JSVAL_TO_INT(id);
	if ((uintN)slot < fp->nvars)
	    fp->vars[slot] = *vp;
    }
    return JS_TRUE;
}

static JSBool
call_enumerate(JSContext *cx, JSObject *obj)
{
    JSStackFrame *fp;
    JSFunction *fun;
    JSScope *scope;
    JSScopeProperty *sprop;
    JSPropertyOp getter;
    JSProperty *prop;

    fp = JS_GetPrivate(cx, obj);
    if (!fp)
	return JS_TRUE;
    fun = fp->fun;
    if (!fun->script || !fun->object)
	return JS_TRUE;

    /* Reflect actual args for formal parameters, and all local variables. */
    scope = (JSScope *)fun->object->map;
    for (sprop = scope->props; sprop; sprop = sprop->next) {
	getter = sprop->getter;
	if (getter != js_GetArgument && getter != js_GetLocalVariable)
	    continue;

	/* Trigger reflection in call_resolve by doing a lookup. */
	if (!js_LookupProperty(cx, obj, sym_id(sprop->symbols), &obj, &prop))
	    return JS_FALSE;
	OBJ_DROP_PROPERTY(cx, obj, prop);
    }

    return JS_TRUE;
}

static JSBool
call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
	     JSObject **objp)
{
    JSStackFrame *fp;
    JSString *str;
    JSAtom *atom;
    JSObject *obj2;
    JSScopeProperty *sprop;
    JSPropertyOp getter, setter;
    jsval propid, *vp;
    jsid symid;
    uintN slot, nslots;

    fp = JS_GetPrivate(cx, obj);
    if (!fp)
	return JS_TRUE;

    if (!JSVAL_IS_STRING(id))
	return JS_TRUE;

    if (!fp->fun->object)
	return JS_TRUE;

    str = JSVAL_TO_STRING(id);
    atom = js_AtomizeString(cx, str, 0);
    if (!atom)
	return JS_FALSE;
    if (!OBJ_LOOKUP_PROPERTY(cx, fp->fun->object, (jsid)atom, &obj2,
			     (JSProperty **)&sprop)) {
	return JS_FALSE;
    }

    if (sprop) {
	getter = sprop->getter;
	propid = sprop->id;
	symid = (jsid) sym_atom(sprop->symbols);
	OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
	if (getter == js_GetArgument || getter == js_GetLocalVariable) {
	    if (getter == js_GetArgument) {
	        vp = fp->argv;
	        nslots = fp->argc;
	        getter = setter = NULL;
            } else {
	        vp = fp->vars;
	        nslots = fp->nvars;
	        getter = js_GetCallVariable;
	        setter = js_SetCallVariable;
	    }
	    slot = (uintN)JSVAL_TO_INT(propid);
	    if (!js_DefineProperty(cx, obj, symid,
				   (slot < nslots) ? vp[slot] : JSVAL_VOID,
				   getter, setter,
				   JSPROP_ENUMERATE | JSPROP_PERMANENT,
				   (JSProperty **)&sprop)) {
		return JS_FALSE;
	    }
	    JS_ASSERT(sprop);
	    if (slot < nslots)
		sprop->id = INT_TO_JSVAL(slot);
	    OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);
	    *objp = obj;
	}
    }
    return JS_TRUE;
}

static JSBool
call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
    JSStackFrame *fp;

    if (type == JSTYPE_FUNCTION) {
	fp = JS_GetPrivate(cx, obj);
	if (fp)
	    *vp = OBJECT_TO_JSVAL(fp->fun->object);
    }
    return JS_TRUE;
}

JSClass js_CallClass = {
    "Call",
    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
    JS_PropertyStub,  JS_PropertyStub,
    call_getProperty, call_setProperty,
    call_enumerate,   (JSResolveOp)call_resolve,
    call_convert,     JS_FinalizeStub
};

#endif /* JS_HAS_CALL_OBJECT */

#if JS_HAS_LEXICAL_CLOSURE
static JSBool
Closure(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSStackFrame *caller;
    JSObject *varParent, *closureParent;
    JSFunction *fun;

    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_ClosureClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(obj);
    }
    if (!(caller = cx->fp->down) || !caller->scopeChain)
	return JS_TRUE;

    varParent = js_FindVariableScope(cx, &fun);
    if (!varParent)
	return JS_FALSE;

    closureParent = caller->scopeChain;
    if (argc != 0) {
	fun = js_ValueToFunction(cx, &argv[0], JS_FALSE);
	if (!fun)
	    return JS_FALSE;
	OBJ_SET_PROTO(cx, obj, fun->object);
	if (argc > 1) {
	    if (!js_ValueToObject(cx, argv[1], &closureParent))
		return JS_FALSE;
	}
    }
    OBJ_SET_PARENT(cx, obj, closureParent);

    /* Make sure constructor is not inherited from fun->object. */
    return js_DefineProperty(cx, obj,
			     (jsid)cx->runtime->atomState.constructorAtom,
			     argv[-2], NULL, NULL,
			     JSPROP_READONLY | JSPROP_PERMANENT,
			     NULL);
}

static JSBool
closure_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
    JSObject *proto;

    if (type == JSTYPE_FUNCTION) {
	proto = OBJ_GET_PROTO(cx, obj);
	if (proto)
	    *vp = OBJECT_TO_JSVAL(proto);
        return JS_TRUE;
    }

    return js_TryValueOf(cx, obj, type, vp);
}

static JSBool
closure_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSStackFrame *fp;
    JSObject *closure, *callobj;
    JSFunction *fun;
    jsval junk;

    /* Get a call object to link the closure's parent into the scope chain. */
    fp = cx->fp;
    closure = JSVAL_TO_OBJECT(argv[-2]);
    JS_ASSERT(OBJ_GET_CLASS(cx, closure) == &js_ClosureClass);
    callobj = js_GetCallObject(cx, fp, OBJ_GET_PARENT(cx, closure), NULL);
    if (!callobj)
	return JS_FALSE;
    fp->scopeChain = callobj;

    /* Make the function object, not its closure, available as argv[-2]. */
    fun = fp->fun;
    argv[-2] = OBJECT_TO_JSVAL(fun->object);
    if (fun->call)
	return fun->call(cx, obj, argc, argv, rval);
    if (fun->script)
	return js_Interpret(cx, &junk);
    return JS_TRUE;
}

JSClass js_ClosureClass = {
    "Closure",
    0,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   closure_convert,  JS_FinalizeStub,
    NULL,             NULL,             closure_call,     closure_call
};
#endif /* JS_HAS_LEXICAL_CLOSURE */

static JSPropertySpec function_props[] = {
    /*
     * We make fun.arguments readonly in fun_setProperty, unless it is being
     * set by an unqualified assignment 'arguments = ...' within a call where
     * fun->object is proxying for a Call object.
     */
    {js_arguments_str,  CALL_ARGUMENTS, JSPROP_PERMANENT},
    {"__arity__",       FUN_ARITY,      JSPROP_READONLY | JSPROP_PERMANENT},
    {"__length__",      ARGS_LENGTH,    JSPROP_READONLY | JSPROP_PERMANENT},
    {"__caller__",      ARGS_CALLER,    JSPROP_READONLY | JSPROP_PERMANENT},
    {"__name__",        FUN_NAME,       JSPROP_READONLY | JSPROP_PERMANENT},
    {"__call__",        FUN_CALL,       JSPROP_READONLY | JSPROP_PERMANENT},
    {0}
};

static JSBool
fun_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    /* Make delete f.length fail even though length is in f.__proto__. */
    if (!JSVAL_IS_INT(id)) {
	if (id == ATOM_KEY(cx->runtime->atomState.arityAtom) ||
	    id == ATOM_KEY(cx->runtime->atomState.lengthAtom) ||
	    id == ATOM_KEY(cx->runtime->atomState.callerAtom) ||
	    id == ATOM_KEY(cx->runtime->atomState.nameAtom)) {
	    *vp = JSVAL_FALSE;
	}
    }
    return JS_TRUE;
}

static JSBool
fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFunction *fun;
    JSStackFrame *fp;
    jsint slot;
#if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800
    /* MSVC1.5 coredumps */
    jsval bogus = *vp;
#endif

    if (!JSVAL_IS_INT(id)) {
	/* Map qualified access of the form f.arity to f.__arity__. */
	if (id == ATOM_KEY(cx->runtime->atomState.arityAtom)) {
	    id = INT_TO_JSVAL(FUN_ARITY);
	} else if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
	    id = INT_TO_JSVAL(ARGS_LENGTH);
	} else if (id == ATOM_KEY(cx->runtime->atomState.callerAtom)) {
	    id = INT_TO_JSVAL(ARGS_CALLER);
	} else if (id == ATOM_KEY(cx->runtime->atomState.nameAtom)) {
	    id = INT_TO_JSVAL(FUN_NAME);
	} else {
	    return JS_TRUE;
	}
    }

    /* No valid function object should lack private data, but check anyway. */
    fun = JS_GetPrivate(cx, obj);
    if (!fun)
	return JS_TRUE;

    /* Find fun's top-most activation record. */
    for (fp = cx->fp; fp && (fp->fun != fun || fp->debugging); fp = fp->down)
	continue;

    slot = (jsint)JSVAL_TO_INT(id);
    switch (slot) {
#if JS_HAS_ARGS_OBJECT
      case CALL_ARGUMENTS:
	if (fp && fp->fun) {
	    JSObject *argsobj = js_GetArgsObject(cx, fp);
	    if (!argsobj)
		return JS_FALSE;
	    *vp = OBJECT_TO_JSVAL(argsobj);
	} else {
	    *vp = JSVAL_NULL;
	}
	break;
#else  /* !JS_HAS_ARGS_OBJECT */
      case CALL_ARGUMENTS:
	*vp = OBJECT_TO_JSVAL(fp ? obj : NULL);
	break;
#endif /* !JS_HAS_ARGS_OBJECT */

      case ARGS_LENGTH:
	if (!JSVERSION_IS_ECMA(cx->version))
	    *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs));
	else
      case FUN_ARITY:
	    *vp = INT_TO_JSVAL((jsint)fun->nargs);
	break;

      case ARGS_CALLER:
	if (fp && fp->down && fp->down->fun)
	    *vp = OBJECT_TO_JSVAL(fp->down->fun->object);
	else
	    *vp = JSVAL_NULL;
	break;

      case FUN_NAME:
	*vp = fun->atom
	      ? ATOM_KEY(fun->atom)
	      : STRING_TO_JSVAL(cx->runtime->emptyString);
	break;

      case FUN_CALL:
	if (fp && fp->fun) {
	    JSObject *callobj = js_GetCallObject(cx, fp, NULL, NULL);
	    if (!callobj)
		return JS_FALSE;
	    *vp = OBJECT_TO_JSVAL(callobj);
	} else {
	    *vp = JSVAL_NULL;
	}
	break;

      default:
	/* XXX fun[0] and fun.arguments[0] are equivalent. */
	if (fp && fp->fun && (uintN)slot < fp->argc)
#if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800
	  /* MSVC1.5 coredumps */
	  if (bogus == *vp)
#endif
	    *vp = fp->argv[slot];
	break;
    }

    return JS_TRUE;
}


static JSBool
fun_enumProperty(JSContext *cx, JSObject *obj)
{
    JSScope *scope;
    JSScopeProperty *sprop;

    /* Because properties of function objects such as "length" are
     * not defined in function_props to avoid interfering with
     * unqualified lookups in local scopes (which pass through the
     * function object as a stand-in for the call object), we
     * must twiddle any of the special properties not to be enumer-
     * ated in this callback, rather than simply predefining the
     * properties without JSPROP_ENUMERATE.
     */

    JS_LOCK_OBJ(cx, obj);
    scope = (JSScope *) obj->map;
    for (sprop = scope->props; sprop; sprop = sprop->next) {
	jsval id = sprop->id;
	if (!JSVAL_IS_INT(id)) {
	    if (id == ATOM_KEY(cx->runtime->atomState.arityAtom) ||
		id == ATOM_KEY(cx->runtime->atomState.lengthAtom) ||
		id == ATOM_KEY(cx->runtime->atomState.callerAtom) ||
		id == ATOM_KEY(cx->runtime->atomState.nameAtom))
	    {
		sprop->attrs &= ~JSPROP_ENUMERATE;
	    }
	}
    }
    return JS_TRUE;
}

static JSBool
fun_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFunction *fun;
    JSStackFrame *fp;
    JSObject *callobj;

    /* Handle only the setting of arguments or fun.arguments in active fun. */
    if (!JSVAL_IS_INT(id) || JSVAL_TO_INT(id) != CALL_ARGUMENTS)
	return JS_TRUE;

    /* No valid function object should lack private data, but check anyway. */
    fun = JS_GetPrivate(cx, obj);
    if (!fun)
	return JS_TRUE;

    /* Find the top-most non-native activation record, which must be fun's. */
    for (fp = cx->fp; ; fp = fp->down) {
	if (!fp)
	    goto _readonly;
	if (fp->fun == fun) {
	    if (!fp->debugging)
		break;
	} else {
	    if (fp->script)
		goto _readonly;
	}
    }

    /* Set only if unqualified: 'arguments = ...' not 'fun.arguments = ...'. */
    if (!fp->pc || (js_CodeSpec[*fp->pc].format & JOF_MODEMASK) != JOF_NAME)
	goto _readonly;

    /* Get a Call object for fp and set its arguments property to vp. */
    callobj = js_GetCallObject(cx, fp, NULL, NULL);
    if (!callobj)
	return JS_FALSE;
    return js_SetProperty(cx, callobj,
			  (jsid)cx->runtime->atomState.argumentsAtom,
			  vp);

/* "readonly" can be a language extension on OSF */
_readonly:
    if (JSVERSION_IS_ECMA(cx->version))
	return fun_getProperty(cx, obj, id, vp);
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY,
			 js_arguments_str);
    return JS_FALSE;
}

static JSBool
fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
	    JSObject **objp)
{
    JSFunction *fun;
    JSString *str;
    JSAtom *prototypeAtom;

    if (!JSVAL_IS_STRING(id))
	return JS_TRUE;

    /* No valid function object should lack private data, but check anyway. */
    fun = JS_GetPrivate(cx, obj);
    if (!fun || !fun->object)
	return JS_TRUE;

    /* No need to reflect fun.prototype in 'fun.prototype = ...'. */
    flags &= JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING;
    if (flags == (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING))
	return JS_TRUE;

    /* Hide prototype if fun->object is proxying for a Call object. */
    if (!(flags & JSRESOLVE_QUALIFIED)) {
	if (cx->fp && cx->fp->fun == fun && !cx->fp->callobj)
	    return JS_TRUE;
    }

    str = JSVAL_TO_STRING(id);
    prototypeAtom = cx->runtime->atomState.classPrototypeAtom;
    if (str == ATOM_TO_STRING(prototypeAtom)) {
	JSObject *proto;
	jsval pval;

	proto = NULL;
	if (fun->object != obj) {
	    /*
	     * Clone of a function: make its prototype property value have the
	     * same class as the clone-parent's prototype.
	     */
	    if (!OBJ_GET_PROPERTY(cx, fun->object, (jsid)prototypeAtom, &pval))
		return JS_FALSE;
	    if (JSVAL_IS_OBJECT(pval))
		proto = JSVAL_TO_OBJECT(pval);
	}

	/* If resolving "prototype" in a clone, clone the parent's prototype. */
	if (proto)
	    proto = js_NewObject(cx, OBJ_GET_CLASS(cx, proto), proto, NULL);
	else
	    proto = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
	if (!proto)
	    return JS_FALSE;

	/*
	 * ECMA says that constructor.prototype is DontEnum for user-defined
	 * functions, but DontEnum | ReadOnly | DontDelete for native "system"
	 * constructors such as Object or Function.  So lazily set the former
	 * here in fun_resolve, but eagerly define the latter in JS_InitClass,
	 * with the right attributes.
	 */
	if (!js_SetClassPrototype(cx, fun->object, proto, 0)) {
	    cx->newborn[GCX_OBJECT] = NULL;
	    return JS_FALSE;
	}
	*objp = obj;
    }

    return JS_TRUE;
}

static JSBool
fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
    switch (type) {
      case JSTYPE_FUNCTION:
	*vp = OBJECT_TO_JSVAL(obj);
	break;
      default:
        return js_TryValueOf(cx, obj, type, vp);
    }
    return JS_TRUE;
}

static void
fun_finalize(JSContext *cx, JSObject *obj)
{
    JSFunction *fun;

    /* No valid function object should lack private data, but check anyway. */
    fun = JS_GetPrivate(cx, obj);
    if (!fun)
	return;
    if (fun->object == obj)
	fun->object = NULL;
    JS_ATOMIC_ADDREF(&fun->nrefs, -1);
    if (fun->nrefs)
	return;
    if (fun->script)
	js_DestroyScript(cx, fun->script);
    JS_free(cx, fun);
}

#if JS_HAS_XDR

#include "jsxdrapi.h"

enum {
    JSXDR_FUNARG = 1,
    JSXDR_FUNVAR = 2
};

/* XXX store parent and proto, if defined */
static JSBool
fun_xdrObject(JSXDRState *xdr, JSObject **objp)
{
    JSFunction *fun;
    JSString *atomstr;
    char *propname;
    JSScopeProperty *sprop;
    JSBool magic;
    jsid propid;
    JSAtom *atom;
    uintN i;
    uint32 type;
#ifdef DEBUG
    uintN nvars = 0, nargs = 0;
#endif

    if (xdr->mode == JSXDR_ENCODE) {
	/*
	 * No valid function object should lack private data, but fail soft
	 * (return true, no error report) in case one does due to API pilot
	 * or internal error.
	 */
	fun = JS_GetPrivate(xdr->cx, *objp);
	if (!fun)
	    return JS_TRUE;
	atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
    } else {
	fun = js_NewFunction(xdr->cx, NULL, NULL, 0, 0, NULL, NULL);
	if (!fun)
	    return JS_FALSE;
    }

    if (!JS_XDRStringOrNull(xdr, &atomstr) ||
	!JS_XDRUint16(xdr, &fun->nargs) ||
	!JS_XDRUint16(xdr, &fun->extra) ||
	!JS_XDRUint16(xdr, &fun->nvars) ||
	!JS_XDRUint8(xdr, &fun->flags))
	return JS_FALSE;

    /* do arguments and local vars */
    if (fun->object) {
	if (xdr->mode == JSXDR_ENCODE) {
	    JSScope *scope = (JSScope *) fun->object->map;

	    for (sprop = scope->props; sprop; sprop = sprop->next) {
		if (sprop->getter == js_GetArgument) {
		    type = JSXDR_FUNARG;
		    JS_ASSERT(nargs++ <= fun->nargs);
		} else if (sprop->getter == js_GetLocalVariable) {
		    type = JSXDR_FUNVAR;
		    JS_ASSERT(nvars++ <= fun->nvars);
		} else {
		    continue;
		}
		propname = ATOM_BYTES(sym_atom(sprop->symbols));
		propid = sprop->id;
		if (!JS_XDRUint32(xdr, &type) ||
		    !JS_XDRUint32(xdr, (uint32 *)&propid) ||
		    !JS_XDRCString(xdr, &propname))
		    return JS_FALSE;
	    }
	} else {
	    JSPropertyOp getter, setter;

	    i = fun->nvars + fun->nargs;
	    while (i--) {
		if (!JS_XDRUint32(xdr, &type) ||
		    !JS_XDRUint32(xdr, (uint32 *)&propid) ||
		    !JS_XDRCString(xdr, &propname))
		    return JS_FALSE;
		JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR);
		if (type == JSXDR_FUNARG) {
		    getter = js_GetArgument;
		    setter = js_SetArgument;
		    JS_ASSERT(nargs++ <= fun->nargs);
		} else if (type == JSXDR_FUNVAR) {
		    getter = js_GetLocalVariable;
		    setter = js_SetLocalVariable;
		    JS_ASSERT(nvars++ <= fun->nvars);
                } else {
                    getter = NULL;
                    setter = NULL;
                }
		atom = js_Atomize(xdr->cx, propname, strlen(propname), 0);
		if (!atom ||
		    !OBJ_DEFINE_PROPERTY(xdr->cx, fun->object, (jsid)atom,
					 JSVAL_VOID, getter, setter,
					 JSPROP_ENUMERATE | JSPROP_PERMANENT,
					 (JSProperty **)&sprop) ||
		    !sprop){
		    JS_free(xdr->cx, propname);
		    return JS_FALSE;
		}
		sprop->id = propid;
		JS_free(xdr->cx, propname);
	    }
	}
    }
    if (!js_XDRScript(xdr, &fun->script, &magic) ||
	!magic)
	return JS_FALSE;

    if (xdr->mode == JSXDR_DECODE) {
	if (atomstr) {
	    fun->atom = js_AtomizeString(xdr->cx, atomstr, 0);
	    if (!fun->atom)
		return JS_FALSE;
	}
	*objp = fun->object;
	if (!OBJ_DEFINE_PROPERTY(xdr->cx, xdr->cx->globalObject,
				 (jsid)fun->atom, OBJECT_TO_JSVAL(*objp),
				 NULL, NULL, JSPROP_ENUMERATE,
				 (JSProperty **)&sprop))
	    return JS_FALSE;
    }

    return JS_TRUE;
}

#else  /* !JS_HAS_XDR */

#define fun_xdrObject NULL

#endif /* !JS_HAS_XDR */

#if JS_HAS_INSTANCEOF

/*
 * [[HasInstance]] internal method for Function objects - takes the .prototype
 * property of its target, and walks the prototype chain of v (if v is an
 * object,) returning true if .prototype is found.
 */
static JSBool
fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
{
    jsval pval;
    JSString *str;

    if (!OBJ_GET_PROPERTY(cx, obj,
			  (jsid)cx->runtime->atomState.classPrototypeAtom,
			  &pval)) {
	return JS_FALSE;
    }
    if (!JSVAL_IS_PRIMITIVE(pval))
	return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp);

    /*
     * Throw a runtime error if instanceof is called on a function
     * that has a non-Object as its .prototype value.
     */
    str = js_DecompileValueGenerator(cx, OBJECT_TO_JSVAL(obj), NULL);
    if (str) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE,
			     JS_GetStringBytes(str));
    }
    return JS_FALSE;

}

#else  /* !JS_HAS_INSTANCEOF */

#define fun_hasInstance NULL

#endif /* !JS_HAS_INSTANCEOF */

JSClass js_FunctionClass = {
    "Function",
    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
    JS_PropertyStub,  fun_delProperty,
    fun_getProperty,  fun_setProperty,
    fun_enumProperty, (JSResolveOp)fun_resolve,
    fun_convert,      fun_finalize,
    NULL,             NULL,
    NULL,             NULL,
    fun_xdrObject,    fun_hasInstance
};

static JSBool
fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval fval;
    JSFunction *fun;
    uint32 indent;
    JSString *str;

    fval = argv[-1];    
    if (!JSVAL_IS_FUNCTION(cx, fval)) {
/*
    if we don't have a function to start off with, try converting the
    object to a function. If that doesn't work, complain.
*/
        if (JSVAL_IS_OBJECT(fval)) {
            obj = JSVAL_TO_OBJECT(fval);
            if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, 
                                                    JSTYPE_FUNCTION, &fval))
	        return JS_FALSE;
        }
        if (!JSVAL_IS_FUNCTION(cx, fval)) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                 JSMSG_INCOMPATIBLE_PROTO,
                                 "Function", "toString", 
                                 JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));
            return JS_FALSE;
        }        
    }

    obj = JSVAL_TO_OBJECT(fval);
    fun = JS_GetPrivate(cx, obj);
    if (!fun)
	return JS_TRUE;
    indent = 0;
    if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
	return JS_FALSE;
    str = JS_DecompileFunction(cx, fun, (uintN)indent);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

#if JS_HAS_CALL_FUNCTION
static JSBool
fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval fval, *sp, *oldsp;
    void *mark;
    uintN i;
    JSStackFrame *fp;
    JSBool ok;

    if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))
	return JS_FALSE;
    fval = argv[-1];

    if (!JSVAL_IS_FUNCTION(cx, fval)) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                             JSMSG_INCOMPATIBLE_PROTO,
                             "Function", "call", 
                             JS_GetStringBytes(JS_ValueToString(cx, fval)));
        return JS_FALSE;
    }        

    if (argc == 0) {
	/* Call fun with its parent as the 'this' parameter if no args. */
	obj = OBJ_GET_PARENT(cx, obj);
    } else {
	/* Otherwise convert the first arg to 'this' and skip over it. */
	if (!js_ValueToObject(cx, argv[0], &obj))
	    return JS_FALSE;
	argc--;
	argv++;
    }

    /* Allocate stack space for fval, obj, and the args. */
    sp = js_AllocStack(cx, 2 + argc, &mark);
    if (!sp)
	return JS_FALSE;

    /* Push fval, obj, and the args. */
    *sp++ = fval;
    *sp++ = OBJECT_TO_JSVAL(obj);
    for (i = 0; i < argc; i++)
	*sp++ = argv[i];

    /* Lift current frame to include the args and do the call. */
    fp = cx->fp;
    oldsp = fp->sp;
    fp->sp = sp;
    ok = js_Invoke(cx, argc, JS_FALSE);

    /* Store rval and pop stack back to our frame's sp. */
    *rval = fp->sp[-1];
    fp->sp = oldsp;
    js_FreeStack(cx, mark);
    return ok;
}
#endif /* JS_HAS_CALL_FUNCTION */

#if JS_HAS_APPLY_FUNCTION
static JSBool
fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval fval, *sp, *oldsp;
    JSObject *aobj;
    jsuint length;
    void *mark;
    uintN i;
    JSBool ok;
    JSStackFrame *fp;

    if (argc != 2 ||
	!JSVAL_IS_OBJECT(argv[1]) ||
	!(aobj = JSVAL_TO_OBJECT(argv[1])) ||
	!js_HasLengthProperty(cx, aobj, &length)) {
	return fun_call(cx, obj, argc, argv, rval);
    }

    if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))
	return JS_FALSE;
    fval = argv[-1];

    if (!JSVAL_IS_FUNCTION(cx, fval)) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                             JSMSG_INCOMPATIBLE_PROTO,
                             "Function", "apply", 
                             JS_GetStringBytes(JS_ValueToString(cx, fval)));
        return JS_FALSE;
    }        
    /* Convert the first arg to 'this' and skip over it. */
    if (!js_ValueToObject(cx, argv[0], &obj))
	return JS_FALSE;

    /* Allocate stack space for fval, obj, and the args. */
    argc = (uintN)length;
    sp = js_AllocStack(cx, 2 + argc, &mark);
    if (!sp)
	return JS_FALSE;

    /* Push fval, obj, and aobj's elements as args. */
    *sp++ = fval;
    *sp++ = OBJECT_TO_JSVAL(obj);
    for (i = 0; i < argc; i++) {
	ok = JS_GetElement(cx, aobj, (jsint)i, sp);
	if (!ok)
	    goto out;
	sp++;
    }

    /* Lift current frame to include the args and do the call. */
    fp = cx->fp;
    oldsp = fp->sp;
    fp->sp = sp;
    ok = js_Invoke(cx, argc, JS_FALSE);

    /* Store rval and pop stack back to our frame's sp. */
    *rval = fp->sp[-1];
    fp->sp = oldsp;
out:
    js_FreeStack(cx, mark);
    return ok;
}
#endif /* JS_HAS_APPLY_FUNCTION */

static JSFunctionSpec function_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   fun_toString,   0},
#endif
    {js_toString_str,	fun_toString,	1},
#if JS_HAS_APPLY_FUNCTION
    {"apply",		fun_apply,	1},
#endif
#if JS_HAS_CALL_FUNCTION
    {"call",		fun_call,	1},
#endif
    {0}
};

JSBool
js_IsIdentifier(JSString *str)
{
    size_t n;
    jschar *s, c;

    n = str->length;
    s = str->chars;
    c = *s;
    if (n == 0 || !JS_ISIDENT(c))
	return JS_FALSE;
    for (n--; n != 0; n--) {
	c = *++s;
	if (!JS_ISIDENT2(c))
	    return JS_FALSE;
    }
    return JS_TRUE;
}

static JSBool
Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFunction *fun;
    JSObject *parent;
    uintN i, n, lineno;
    JSAtom *atom;
    const char *filename;
    JSObject *obj2;
    JSScopeProperty *sprop;
    JSString *str, *arg;
    JSStackFrame *fp;
    void *mark;
    JSTokenStream *ts;
    JSPrincipals *principals;
    jschar *collected_args, *cp;
    size_t args_length;
    JSTokenType tt;
    JSBool ok;

    if (cx->fp && !cx->fp->constructing) {
	obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(obj);
    }
    fun = JS_GetPrivate(cx, obj);
    if (fun)
	return JS_TRUE;

#if JS_HAS_CALL_OBJECT
    /*
     * NB: (new Function) is not lexically closed by its caller, it's just an
     * anonymous function in the top-level scope that its constructor inhabits.
     * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
     * and so would a call to f from another top-level's script or function.
     *
     * In older versions, before call objects, a new Function was adopted by
     * its running context's globalObject, which might be different from the
     * top-level reachable from scopeChain (in HTML frames, e.g.).
     */
    parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
#else
    /* Set up for dynamic parenting (see Call in jsinterp.c). */
    parent = NULL;
#endif

    fun = js_NewFunction(cx, obj, NULL, 0, 0, parent,
			 (JSVERSION_IS_ECMA(cx->version))
			 ? cx->runtime->atomState.anonymousAtom
			 : NULL);

    if (!fun)
	return JS_FALSE;

    if ((fp = cx->fp) != NULL && (fp = fp->down) != NULL && fp->script) {
	filename = fp->script->filename;
	lineno = js_PCToLineNumber(fp->script, fp->pc);
	principals = fp->script->principals;
    } else {
	filename = NULL;
	lineno = 0;
	principals = NULL;
    }

    n = argc ? argc - 1 : 0;
    if (n > 0) {
	/*
	 * Collect the function-argument arguments into one string, separated
	 * by commas, then make a tokenstream from that string, and scan it to
	 * get the arguments.  We need to throw the full scanner at the
	 * problem, because the argument string can legitimately contain
	 * comments and linefeeds.  XXX It might be better to concatenate
	 * everything up into a function definition and pass it to the
	 * compiler, but doing it this way is less of a delta from the old
	 * code.  See ECMA 15.3.2.1.
	 */
	args_length = 0;
	for (i = 0; i < n; i++) {
	    /* Collect the lengths for all the function-argument arguments. */
	    arg = JSVAL_TO_STRING(argv[i]);
	    args_length += arg->length;
	}
	/* Add 1 for each joining comma. */
	args_length += n - 1;

	/*
	 * Allocate a string to hold the concatenated arguments, including room
	 * for a terminating 0.  Mark cx->tempPool for later release, to free
	 * collected_args and its tokenstream in one swoop.
	 */
	mark = JS_ARENA_MARK(&cx->tempPool);
	JS_ARENA_ALLOCATE(cp, &cx->tempPool, (args_length+1) * sizeof(jschar));
	if (!cp)
	    return JS_FALSE;
	collected_args = cp;

	/*
	 * Concatenate the arguments into the new string, separated by commas.
	 */
	for (i = 0; i < n; i++) {
	    arg = JSVAL_TO_STRING(argv[i]);
	    (void) js_strncpy(cp, arg->chars, arg->length);
	    cp += arg->length;

	    /* Add separating comma or terminating 0. */
	    *cp++ = (i + 1 < n) ? ',' : 0;
	}

	/*
	 * Make a tokenstream (allocated from cx->tempPool) that reads from
	 * the given string.
	 */
	ts = js_NewTokenStream(cx, collected_args, args_length, filename,
			       lineno, principals);
	if (!ts) {
	    JS_ARENA_RELEASE(&cx->tempPool, mark);
	    return JS_FALSE;
	}

	/* The argument string may be empty or contain no tokens. */
	tt = js_GetToken(cx, ts);
	if (tt != TOK_EOF) {
	    while (1) {
		/*
		 * Check that it's a name.  This also implicitly guards against
		 * TOK_ERROR, which was already reported.
		 */
		if (tt != TOK_NAME)
		    goto bad_formal;

		/*
		 * Get the atom corresponding to the name from the tokenstream;
		 * we're assured at this point that it's a valid identifier.
		 */
		atom = ts->token.t_atom;
		if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2,
				       (JSProperty **)&sprop)) {
		    goto bad_formal;
		}
		if (sprop && obj2 == obj) {
#ifdef CHECK_ARGUMENT_HIDING
		    JS_ASSERT(sprop->getter == js_GetArgument);
		    OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
		    JS_ReportErrorNumber(cx, JSREPORT_WARNING,
					 JSMSG_SAME_FORMAL, ATOM_BYTES(atom));
		    goto bad_formal;
#else
		    /*
		     * A duplicate parameter name. We create a dummy symbol
		     * entry with property id of the parameter number and set
		     * the id to the name of the parameter.  See jsopcode.c:
		     * the decompiler knows to treat this case specially.
		     */
		    jsid oldArgId = (jsid) sprop->id;
		    OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
		    sprop = NULL;
		    if (!js_DefineProperty(cx, obj, oldArgId, JSVAL_VOID,
					   js_GetArgument, js_SetArgument,
					   JSPROP_ENUMERATE | JSPROP_PERMANENT,
					   (JSProperty **)&sprop)) {
			goto bad_formal;
		    }
		    sprop->id = (jsid) atom;
#endif
		}
		if (sprop)
		    OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
		if (!js_DefineProperty(cx, obj, (jsid)atom, JSVAL_VOID,
				       js_GetArgument, js_SetArgument,
				       JSPROP_ENUMERATE | JSPROP_PERMANENT,
				       (JSProperty **)&sprop)) {
		    goto bad_formal;
		}
		JS_ASSERT(sprop);
		sprop->id = INT_TO_JSVAL(fun->nargs++);
		OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);

		/*
		 * Get the next token.  Stop on end of stream.  Otherwise
		 * insist on a comma, get another name, and iterate.
		 */
		tt = js_GetToken(cx, ts);
		if (tt == TOK_EOF)
		    break;
		if (tt != TOK_COMMA)
		    goto bad_formal;
		tt = js_GetToken(cx, ts);
	    }
	}

	/* Clean up. */
	ok = js_CloseTokenStream(cx, ts);
	JS_ARENA_RELEASE(&cx->tempPool, mark);
	if (!ok)
	    return JS_FALSE;
    }

    if (argc) {
	str = js_ValueToString(cx, argv[argc-1]);
    } else {
	/* Can't use cx->runtime->emptyString because we're called too early. */
	str = js_NewStringCopyZ(cx, js_empty_ucstr, 0);
    }
    if (!str)
	return JS_FALSE;
    if (argv) {
	/* Use the last arg (or this if argc == 0) as a local GC root. */
	argv[(intn)(argc-1)] = STRING_TO_JSVAL(str);
    }

    if ((fp = cx->fp) != NULL && (fp = fp->down) != NULL && fp->script) {
	filename = fp->script->filename;
	lineno = js_PCToLineNumber(fp->script, fp->pc);
	principals = fp->script->principals;
    } else {
	filename = NULL;
	lineno = 0;
	principals = NULL;
    }

    mark = JS_ARENA_MARK(&cx->tempPool);
    ts = js_NewTokenStream(cx, str->chars, str->length, filename, lineno,
			   principals);
    if (!ts) {
	ok = JS_FALSE;
    } else {
	ok = js_ParseFunctionBody(cx, ts, fun) &&
	     js_CloseTokenStream(cx, ts);
    }
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return ok;

bad_formal:
    /*
     * Report "malformed formal parameter" iff no illegal char or similar
     * scanner error was already reported.
     */
    if (!(ts->flags & TSF_ERROR))
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL);

    /*
     * Clean up the arguments string and tokenstream if we failed to parse
     * the arguments.
     */
    (void)js_CloseTokenStream(cx, ts);
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return JS_FALSE;
}

JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj)
{
    JSObject *proto;
    JSAtom *atom;
    JSFunction *fun;

    proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
			 function_props, function_methods, NULL, NULL);
    if (!proto)
	return NULL;
    atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name),
		      0);
    if (!atom)
	goto bad;
    fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, atom);
    if (!fun)
	goto bad;
    fun->script = js_NewScript(cx, 0);
    if (!fun->script)
	goto bad;
    return proto;

bad:
    cx->newborn[GCX_OBJECT] = NULL;
    return NULL;
}

JSBool
js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj,
			      JSObject *objProto)
{
#if JS_HAS_ARGS_OBJECT
    if (!JS_InitClass(cx, obj, objProto, &js_ArgumentsClass, Arguments, 0,
		      args_props, NULL, NULL, NULL)) {
	return JS_FALSE;
    }
#endif

#if JS_HAS_CALL_OBJECT
    if (!JS_InitClass(cx, obj, NULL, &js_CallClass, Call, 0,
		      call_props, NULL, NULL, NULL)) {
	return JS_FALSE;
    }
#endif

#if JS_HAS_LEXICAL_CLOSURE
    if (!JS_InitClass(cx, obj, NULL, &js_ClosureClass, Closure, 0,
		      NULL, NULL, NULL, NULL)) {
	return JS_FALSE;
    }
#endif

    return JS_TRUE;
}

JSFunction *
js_NewFunction(JSContext *cx, JSObject *funobj, JSNative call, uintN nargs,
	       uintN flags, JSObject *parent, JSAtom *atom)
{
    JSFunction *fun;

    /* Allocate a function struct. */
    fun = JS_malloc(cx, sizeof *fun);
    if (!fun)
	return NULL;
    fun->nrefs = 0;
    fun->object = NULL;

    /* If funobj is null, allocate an object for it. */
    if (!funobj) {
	funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent);
	if (!funobj) {
	    JS_free(cx, fun);
	    return NULL;
	}
    } else {
	OBJ_SET_PARENT(cx, funobj, parent);
    }

    /* Link fun to funobj and vice versa. */
    if (!js_LinkFunctionObject(cx, fun, funobj)) {
	cx->newborn[GCX_OBJECT] = NULL;
	JS_free(cx, fun);
	return NULL;
    }

    /* Initialize remaining function members. */
    fun->call = call;
    fun->nargs = nargs;
    fun->flags = flags & JSFUN_FLAGS_MASK;
    fun->extra = 0;
    fun->nvars = 0;
    fun->spare = 0;
    fun->atom = atom;
    fun->script = NULL;
    fun->clasp = NULL;
    return fun;
}

JSBool
js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object)
{
    if (!fun->object)
	fun->object = object;
    if (!JS_SetPrivate(cx, object, fun))
	return JS_FALSE;
    JS_ATOMIC_ADDREF(&fun->nrefs, 1);
    return JS_TRUE;
}

JSFunction *
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative call,
		  uintN nargs, uintN attrs)
{
    JSFunction *fun;

    fun = js_NewFunction(cx, NULL, call, nargs, attrs, obj, atom);
    if (!fun)
	return NULL;
    if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(fun->object),
			     NULL, NULL, attrs, NULL)) {
	return NULL;
    }
    return fun;
}

JSFunction *
js_ValueToFunction(JSContext *cx, jsval *vp, JSBool constructing)
{
    jsval v;
    JSObject *obj;

    v = *vp;
    obj = NULL;
    if (JSVAL_IS_OBJECT(v)) {
	obj = JSVAL_TO_OBJECT(v);
	if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
	    if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
		return NULL;
	    obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
	}
    }
    if (!obj) {
	js_ReportIsNotFunction(cx, vp, constructing);
	return NULL;
    }
    return JS_GetPrivate(cx, obj);
}

void
js_ReportIsNotFunction(JSContext *cx, jsval *vp, JSBool constructing)
{
    JSStackFrame *fp;
    JSString *str;
    const char *typeName;
    JSString *fallback;

    fp = cx->fp;
    /*
     * We provide the typename as the fallback to handle the case
     * when valueOf is not a function, which prevents ValueToString
     * from being called as the default case inside 
     * js_DecompileValueGenerator (and so recursing back to here).
     */
    typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, *vp));
    fallback = JS_InternString(cx, typeName);
    if (fp) {
        jsval *sp = fp->sp;
        fp->sp = vp;
        str = js_DecompileValueGenerator(cx, *vp, fallback);
        fp->sp = sp;
    } else {
        str = js_DecompileValueGenerator(cx, *vp, fallback);
    }
    if (str) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     constructing ? JSMSG_NOT_CONSTRUCTOR
					  : JSMSG_NOT_FUNCTION,
			     JS_GetStringBytes(str));
    }
}

**** End of jsfun.c. ****

**** Start of jsfun.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsfun_h___
#define jsfun_h___
/*
 * JS function definitions.
 */
#include "jsprvtd.h"
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

struct JSFunction {
    jsrefcount	 nrefs;		/* number of referencing objects */
    JSObject     *object;       /* back-pointer to GC'ed object header */
    JSNative     call;          /* native method pointer or null */
    uint16       nargs;         /* minimum number of actual arguments */
    uint16       extra;         /* number of arg slots for local GC roots */
    uint16       nvars;         /* number of local variables */
    uint8        flags;         /* bound method and other flags, see jsapi.h */
    uint8        spare;         /* reserved for future use */
    JSAtom       *atom;         /* name for diagnostics and decompiling */
    JSScript     *script;       /* interpreted bytecode descriptor or null */
    JSClass      *clasp;        /* this function is a constructor for objects 
                                 * of this class */
};

extern JSClass js_ArgumentsClass;
extern JSClass js_CallClass;
extern JSClass js_ClosureClass;
/* JS_FRIEND_DATA so that JSVAL_IS_FUNCTION is callable from outside */
extern JS_FRIEND_DATA(JSClass) js_FunctionClass;

/*
 * NB: jsapi.h and jsobj.h must be included before any call to this macro.
 */
#define JSVAL_IS_FUNCTION(cx, v)                                              \
    (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) &&                              \
     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass)

extern JSBool
js_IsIdentifier(JSString *str);

extern JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj);

extern JSBool
js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj,
			      JSObject *objProto);

extern JSFunction *
js_NewFunction(JSContext *cx, JSObject *funobj, JSNative call, uintN nargs,
	       uintN flags, JSObject *parent, JSAtom *atom);

extern JSBool
js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object);

extern JSFunction *
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative call,
		  uintN nargs, uintN flags);

extern JSFunction *
js_ValueToFunction(JSContext *cx, jsval *vp, JSBool constructing);

extern void
js_ReportIsNotFunction(JSContext *cx, jsval *vp, JSBool constructing);

extern JSObject *
js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent,
		 JSObject *withobj);

extern JSBool
js_PutCallObject(JSContext *cx, JSStackFrame *fp);

extern JSBool
js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);

extern JSBool
js_PutArgsObject(JSContext *cx, JSStackFrame *fp);

extern JSBool
js_XDRFunction(JSXDRState *xdr, JSObject **objp);

JS_END_EXTERN_C

#endif /* jsfun_h___ */

**** End of jsfun.h. ****

**** Start of jsgc.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS Mark-and-Sweep Garbage Collector.
 *
 * This GC allocates only fixed-sized things big enough to contain two words
 * (pointers) on any host architecture.  It allocates from an arena pool (see
 * jsarena.h).  It uses a parallel arena-pool array of flag bytes to hold the
 * mark bit, finalizer type index, etc.
 *
 * XXX swizzle page to freelist for better locality of reference
 */
#include "jsstddef.h"
#include <stdlib.h>     /* for free, called by JS_ARENA_DESTROY */
#include <string.h>	/* for memset, called by jsarena.h macros if DEBUG */
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jshash.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

/*
 * Arena sizes, the first must be a multiple of the second so the two arena
 * pools can be maintained (in particular, arenas may be destroyed from the
 * middle of each pool) in parallel.
 */
#define GC_ARENA_SIZE	8192		/* 1024 (512 on Alpha) objects */
#define GC_FLAGS_SIZE	(GC_ARENA_SIZE / sizeof(JSGCThing))
#define GC_ROOTS_SIZE	256		/* SWAG, small enough to amortize */

static JSHashNumber   gc_hash_root(const void *key);

struct JSGCThing {
    JSGCThing       *next;
    uint8           *flagp;
};

typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing);

static GCFinalizeOp gc_finalizers[GCX_NTYPES];

#ifdef JS_GCMETER
#define METER(x) x
#else
#define METER(x) /* nothing */
#endif

JSBool
js_InitGC(JSRuntime *rt, uint32 maxbytes)
{
    if (!gc_finalizers[GCX_OBJECT]) {
	gc_finalizers[GCX_OBJECT] = (GCFinalizeOp)js_FinalizeObject;
	gc_finalizers[GCX_STRING] = (GCFinalizeOp)js_FinalizeString;
	gc_finalizers[GCX_DOUBLE] = (GCFinalizeOp)js_FinalizeDouble;
    }

    JS_InitArenaPool(&rt->gcArenaPool, "gc-arena", GC_ARENA_SIZE,
		     sizeof(JSGCThing));
    JS_InitArenaPool(&rt->gcFlagsPool, "gc-flags", GC_FLAGS_SIZE,
		     sizeof(uint8));
    rt->gcRootsHash = JS_NewHashTable(GC_ROOTS_SIZE, gc_hash_root,
				      JS_CompareValues, JS_CompareValues,
				      NULL, NULL);
    if (!rt->gcRootsHash)
	return JS_FALSE;
    rt->gcMaxBytes = maxbytes;
    return JS_TRUE;
}

#ifdef JS_GCMETER
void
js_DumpGCStats(JSRuntime *rt, FILE *fp)
{
    fprintf(fp, "\nGC allocation statistics:\n");
    fprintf(fp, "     bytes currently allocated: %lu\n", rt->gcBytes);
    fprintf(fp, "                alloc attempts: %lu\n", rt->gcStats.alloc);
    fprintf(fp, "            GC freelist length: %lu\n", rt->gcStats.freelen);
    fprintf(fp, "  recycles through GC freelist: %lu\n", rt->gcStats.recycle);
    fprintf(fp, "alloc retries after running GC: %lu\n", rt->gcStats.retry);
    fprintf(fp, "           allocation failures: %lu\n", rt->gcStats.fail);
    fprintf(fp, "              valid lock calls: %lu\n", rt->gcStats.lock);
    fprintf(fp, "            valid unlock calls: %lu\n", rt->gcStats.unlock);
    fprintf(fp, "   locks that hit stuck counts: %lu\n", rt->gcStats.stuck);
    fprintf(fp, " unlocks that saw stuck counts: %lu\n", rt->gcStats.unstuck);
    fprintf(fp, "          mark recursion depth: %lu\n", rt->gcStats.depth);
    fprintf(fp, "  maximum mark recursion depth: %lu\n", rt->gcStats.maxdepth);
    fprintf(fp, "      maximum GC nesting level: %lu\n", rt->gcStats.maxlevel);
    fprintf(fp, "   potentially useful GC calls: %lu\n", rt->gcStats.poke);
    fprintf(fp, "              useless GC calls: %lu\n", rt->gcStats.nopoke);
    fprintf(fp, "        thing arena corruption: %lu\n", rt->gcStats.badarena);
    fprintf(fp, "        flags arena corruption: %lu\n", rt->gcStats.badflag);
    fprintf(fp, "     thing arenas freed so far: %lu\n", rt->gcStats.afree);
    fprintf(fp, "     flags arenas freed so far: %lu\n", rt->gcStats.fafree);
#ifdef JS_ARENAMETER
    JS_DumpArenaStats(fp);
#endif
}
#endif

void
js_FinishGC(JSRuntime *rt)
{
#ifdef JS_ARENAMETER
    JS_DumpArenaStats(stdout);
#endif
#ifdef JS_GCMETER
    js_DumpGCStats(rt, stdout);
#endif
    JS_FinishArenaPool(&rt->gcArenaPool);
    JS_FinishArenaPool(&rt->gcFlagsPool);
    JS_ArenaFinish();
    JS_HashTableDestroy(rt->gcRootsHash);
    rt->gcRootsHash = NULL;
    rt->gcFreeList = NULL;
}

JSBool
js_AddRoot(JSContext *cx, void *rp, const char *name)
{
    JSRuntime *rt;
    JSBool ok;

    rt = cx->runtime;
    JS_LOCK_GC_VOID(rt,
	ok = (JS_HashTableAdd(rt->gcRootsHash, rp, (void *)name) != NULL));
    if (!ok)
	JS_ReportOutOfMemory(cx);
    return ok;
}

JSBool
js_RemoveRoot(JSContext *cx, void *rp)
{
    JSRuntime *rt;

    rt = cx->runtime;
    JS_LOCK_GC_VOID(rt, JS_HashTableRemove(rt->gcRootsHash, rp));
    return JS_TRUE;
}

void *
js_AllocGCThing(JSContext *cx, uintN flags)
{
    JSRuntime *rt;
    JSGCThing *thing;
    uint8 *flagp = NULL;
#ifdef TOO_MUCH_GC
    JSBool tried_gc = JS_TRUE;
    js_GC(cx);
#else
    JSBool tried_gc = JS_FALSE;
#endif

    rt = cx->runtime;
    JS_LOCK_GC(rt);
    METER(rt->gcStats.alloc++);
retry:
    thing = rt->gcFreeList;
    if (thing) {
	rt->gcFreeList = thing->next;
	flagp = thing->flagp;
	METER(rt->gcStats.freelen--);
	METER(rt->gcStats.recycle++);
    } else {
	if (rt->gcBytes < rt->gcMaxBytes) {
	    JS_ARENA_ALLOCATE(thing, &rt->gcArenaPool, sizeof(JSGCThing));
	    JS_ARENA_ALLOCATE(flagp, &rt->gcFlagsPool, sizeof(uint8));
	}
	if (!thing || !flagp) {
	    if (thing)
		JS_ARENA_RELEASE(&rt->gcArenaPool, thing);
	    if (!tried_gc) {
		JS_UNLOCK_GC(rt);
		js_GC(cx);
		tried_gc = JS_TRUE;
		JS_LOCK_GC(rt);
		METER(rt->gcStats.retry++);
		goto retry;
	    }
	    METER(rt->gcStats.fail++);
	    JS_UNLOCK_GC(rt);
	    JS_ReportOutOfMemory(cx);
	    return NULL;
	}
    }
    *flagp = (uint8)flags;
    rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8);
    cx->newborn[flags & GCF_TYPEMASK] = thing;

    /*
     * Clear thing before unlocking in case a GC run is about to scan it,
     * finding it via cx->newborn[].
     */
    thing->next = NULL;
    thing->flagp = NULL;
    JS_UNLOCK_GC(rt);
    return thing;
}

static uint8 *
gc_find_flags(JSRuntime *rt, void *thing)
{
    jsuword index, offset, length;
    JSArena *a, *fa;

    index = 0;
    for (a = rt->gcArenaPool.first.next; a; a = a->next) {
	offset = JS_UPTRDIFF(thing, a->base);
	length = a->avail - a->base;
	if (offset < length) {
	    index += offset / sizeof(JSGCThing);
	    for (fa = rt->gcFlagsPool.first.next; fa; fa = fa->next) {
		offset = fa->avail - fa->base;
		if (index < offset)
		    return (uint8 *)fa->base + index;
		index -= offset;
	    }
	    return NULL;
	}
	index += length / sizeof(JSGCThing);
    }
    return NULL;
}

JSBool
js_LockGCThing(JSContext *cx, void *thing)
{
    uint8 *flagp, flags;

    if (!thing)
	return JS_TRUE;
    flagp = gc_find_flags(cx->runtime, thing);
    if (!flagp)
	return JS_FALSE;
    flags = *flagp;
    if ((flags & GCF_LOCKMASK) != GCF_LOCKMASK) {
	*flagp = (uint8)(flags + GCF_LOCK);
    } else {
	METER(cx->runtime->gcStats.stuck++);
    }
    METER(cx->runtime->gcStats.lock++);
    return JS_TRUE;
}

JSBool
js_UnlockGCThing(JSContext *cx, void *thing)
{
    uint8 *flagp, flags;

    if (!thing)
	return JS_TRUE;
    flagp = gc_find_flags(cx->runtime, thing);
    if (!flagp)
	return JS_FALSE;
    flags = *flagp;
    if ((flags & GCF_LOCKMASK) != GCF_LOCKMASK) {
	*flagp = (uint8)(flags - GCF_LOCK);
    } else {
	METER(cx->runtime->gcStats.unstuck++);
    }
    METER(cx->runtime->gcStats.unlock++);
    return JS_TRUE;
}

#ifdef GC_MARK_DEBUG

#include <stdio.h>
#include <stdlib.h>
#include "jsprf.h"

JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
JS_FRIEND_DATA(void *) js_LiveThingToFind;

typedef struct GCMarkNode GCMarkNode;

struct GCMarkNode {
    void        *thing;
    char        *name;
    GCMarkNode  *next;
    GCMarkNode  *prev;
};

static void
gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp)
{
    GCMarkNode *next = NULL;
    char *path = NULL;
    JSObject *obj;
    JSClass *clasp;

    while (prev) {
	next = prev;
	prev = prev->prev;
    }
    while (next) {
	path = JS_sprintf_append(path, "%s.", next->name);
	next = next->next;
    }
    if (!path)
	return;

    fprintf(fp, "%08lx ", (long)thing);
    switch (flags & GCF_TYPEMASK) {
      case GCX_OBJECT:
	obj = (JSObject *)thing;
	clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]);
	fprintf(fp, "object %s", clasp->name);
	break;
      case GCX_STRING:
	fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing));
	break;
      case GCX_DOUBLE:
	fprintf(fp, "double %g", *(jsdouble *)thing);
	break;
      case GCX_DECIMAL:
	break;
    }
    fprintf(fp, " via %s\n", path);
    free(path);
}

static void
gc_mark_node(JSRuntime *rt, void *thing, GCMarkNode *prev);

#define GC_MARK(_rt, _thing, _name, _prev)                                    \
    JS_BEGIN_MACRO                                                            \
	GCMarkNode _node;                                                     \
	_node.thing = _thing;                                                 \
	_node.name  = _name;                                                  \
	_node.next  = NULL;                                                   \
	_node.prev  = _prev;                                                  \
	if (_prev) ((GCMarkNode *)(_prev))->next = &_node;                    \
	gc_mark_node(_rt, _thing, &_node);                                    \
    JS_END_MACRO

static void
gc_mark(JSRuntime *rt, void *thing)
{
    GC_MARK(rt, thing, "atom", NULL);
}

#define GC_MARK_ATOM(rt, atom, prev)     gc_mark_atom(rt, atom, prev)
#define GC_MARK_SCRIPT(rt, script, prev) gc_mark_script(rt, script, prev)

#else  /* !GC_MARK_DEBUG */

#define GC_MARK(rt, thing, name, prev)   gc_mark(rt, thing)
#define GC_MARK_ATOM(rt, atom, prev)     gc_mark_atom(rt, atom)
#define GC_MARK_SCRIPT(rt, script, prev) gc_mark_script(rt, script)

static void
gc_mark(JSRuntime *rt, void *thing);

#endif /* !GC_MARK_DEBUG */

static void
gc_mark_atom(JSRuntime *rt, JSAtom *atom
#ifdef GC_MARK_DEBUG
  , GCMarkNode *prev
#endif
)
{
    jsval key;

    if (!atom || atom->flags & ATOM_MARK)
	return;
    atom->flags |= ATOM_MARK;
    key = ATOM_KEY(atom);
    if (JSVAL_IS_GCTHING(key)) {
#ifdef GC_MARK_DEBUG
	char name[32];

	if (JSVAL_IS_STRING(key)) {
	    JS_snprintf(name, sizeof name, "'%s'",
			JS_GetStringBytes(JSVAL_TO_STRING(key)));
	} else {
	    JS_snprintf(name, sizeof name, "<%x>", key);
	}
#endif
	GC_MARK(rt, JSVAL_TO_GCTHING(key), name, prev);
    }
}

static void
gc_mark_script(JSRuntime *rt, JSScript *script
#ifdef GC_MARK_DEBUG
  , GCMarkNode *prev
#endif
)
{
    JSAtomMap *map;
    uintN i, length;
    JSAtom **vector;

    map = &script->atomMap;
    length = map->length;
    vector = map->vector;
    for (i = 0; i < length; i++)
	GC_MARK_ATOM(rt, vector[i], prev);
}

static void
#ifdef GC_MARK_DEBUG
gc_mark_node(JSRuntime *rt, void *thing, GCMarkNode *prev)
#else
gc_mark(JSRuntime *rt, void *thing)
#endif
{
    uint8 flags, *flagp;
    JSObject *obj;
    jsval v, *vp, *end;
    JSScope *scope;
    JSClass *clasp;
    JSScript *script;
    JSFunction *fun;
    JSScopeProperty *sprop;
    JSSymbol *sym;

    if (!thing)
	return;
    flagp = gc_find_flags(rt, thing);
    if (!flagp)
	return;

    /* Check for something on the GC freelist to handle recycled stack. */
    flags = *flagp;
    if (flags == GCF_FINAL)
	return;

#ifdef GC_MARK_DEBUG
    if (js_LiveThingToFind == thing)
	gc_dump_thing(thing, flags, prev, stderr);
#endif

    if (flags & GCF_MARK)
	return;
    *flagp |= GCF_MARK;
    METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth)
	      rt->gcStats.maxdepth = rt->gcStats.depth);

#ifdef GC_MARK_DEBUG
    if (js_DumpGCHeap)
	gc_dump_thing(thing, flags, prev, js_DumpGCHeap);
#endif

    if ((flags & GCF_TYPEMASK) == GCX_OBJECT) {
	obj = thing;
	vp = obj->slots;
	if (vp) {
	    scope = OBJ_IS_NATIVE(obj) ? (JSScope *) obj->map : NULL;
	    if (scope) {
		clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]);

		if (clasp == &js_ScriptClass) {
		    v = vp[JSSLOT_PRIVATE];
		    if (!JSVAL_IS_VOID(v)) {
			script = JSVAL_TO_PRIVATE(v);
			if (script)
			    GC_MARK_SCRIPT(rt, script, prev);
		    }
		}

		if (clasp == &js_FunctionClass) {
		    v = vp[JSSLOT_PRIVATE];
		    if (!JSVAL_IS_VOID(v)) {
			fun = JSVAL_TO_PRIVATE(v);
			if (fun) {
			    if (fun->atom)
				GC_MARK_ATOM(rt, fun->atom, prev);
			    if (fun->script)
				GC_MARK_SCRIPT(rt, fun->script, prev);
			}
		    }
		}

		for (sprop = scope->props; sprop; sprop = sprop->next) {
		    for (sym = sprop->symbols; sym; sym = sym->next) {
			if (JSVAL_IS_INT(sym_id(sym)))
			    continue;
			GC_MARK_ATOM(rt, sym_atom(sym), prev);
		    }
		}
	    }
	    if (!scope || scope->object == obj)
		end = vp + obj->map->freeslot;
	    else
		end = vp + JS_INITIAL_NSLOTS;
	    for (; vp < end; vp++) {
		v = *vp;
		if (JSVAL_IS_GCTHING(v)) {
#ifdef GC_MARK_DEBUG
		    char name[32];

		    if (scope) {
			uint32 slot;
			jsval nval;

			slot = vp - obj->slots;
			for (sprop = scope->props; ; sprop = sprop->next) {
			    if (!sprop) {
				switch (slot) {
				  case JSSLOT_PROTO:
				    strcpy(name, "__proto__");
				    break;
				  case JSSLOT_PARENT:
				    strcpy(name, "__parent__");
				    break;
				  case JSSLOT_PRIVATE:
				    strcpy(name, "__private__");
				    break;
				  default:
				    JS_snprintf(name, sizeof name,
						"**UNKNOWN SLOT %ld**",
						(long)slot);
				    break;
				}
				break;
			    }
			    if (sprop->slot == slot) {
				nval = sprop->symbols
				       ? js_IdToValue(sym_id(sprop->symbols))
				       : sprop->id;
				if (JSVAL_IS_INT(nval)) {
				    JS_snprintf(name, sizeof name, "%ld",
						(long)JSVAL_TO_INT(nval));
				} else if (JSVAL_IS_STRING(nval)) {
				    JS_snprintf(name, sizeof name, "%s",
				      JS_GetStringBytes(JSVAL_TO_STRING(nval)));
				} else {
				    strcpy(name, "**FINALIZED ATOM KEY**");
				}
				break;
			    }
			}
		    }
#endif
		    GC_MARK(rt, JSVAL_TO_GCTHING(v), name, prev);
		}
	    }
	}
    }

    METER(rt->gcStats.depth--);
}

static JSHashNumber
gc_hash_root(const void *key)
{
    JSHashNumber num = (JSHashNumber) key;	/* help lame MSVC1.5 on Win16 */

    return num >> 2;
}

JS_STATIC_DLL_CALLBACK(intN)
gc_root_marker(JSHashEntry *he, intN i, void *arg)
{
    void **rp = (void **)he->key;

    if (*rp) {
#ifdef INTENSE_DEBUG
	JSArena *a;
	JSRuntime *rt = (JSRuntime *)arg;
    JSBool root_points_to_gcArenaPool = JS_FALSE;

    if (rp && *rp) {
        for (a = rt->gcArenaPool.first.next; a; a = a->next) {
            if (*rp >= (void *)a->base && *rp <= (void *)a->avail) {
                root_points_to_gcArenaPool = JS_TRUE;
                break;
            }
        }
        JS_ASSERT(root_points_to_gcArenaPool);
    }
#endif
	GC_MARK(arg, *rp, he->value ? he->value : "root", NULL);
    }
    return HT_ENUMERATE_NEXT;
}

JS_FRIEND_API(void)
js_ForceGC(JSContext *cx)
{
    cx->newborn[GCX_OBJECT] = NULL;
    cx->newborn[GCX_STRING] = NULL;
    cx->newborn[GCX_DOUBLE] = NULL;
    cx->runtime->gcPoke = JS_TRUE;
    js_GC(cx);
    JS_ArenaFinish();
}

void
js_GC(JSContext *cx)
{
    JSRuntime *rt;
    JSContext *iter, *acx;
    JSArena *a, *ma, *fa, **ap, **fap;
    jsval v, *vp, *sp;
    jsuword begin, end;
    JSStackFrame *fp, *chain;
    void *mark;
    uint8 flags, *flagp;
    JSGCThing *thing, *final, **flp, **oflp;
    GCFinalizeOp finalizer;
    JSBool a_all_clear, f_all_clear;

    /*
     * XXX kludge for pre-ECMAv2 compile-time switch case expr eval, see
     * jsemit.c:js_EmitTree, under case TOK_SWITCH: (look for XXX).
     */
    if (cx->gcDisabled)
	return;

    rt = cx->runtime;
#ifdef JS_THREADSAFE
    /* Avoid deadlock. */
    JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));
#endif

    /* Let the API user decide to defer a GC if it wants to. */
    if (rt->gcCallback && !rt->gcCallback(cx, JSGC_BEGIN))
	return;

    /* Lock out other GC allocator and collector invocations. */
    JS_LOCK_GC(rt);

    /* Do nothing if no assignment has executed since the last GC. */
    if (!rt->gcPoke) {
	METER(rt->gcStats.nopoke++);
	JS_UNLOCK_GC(rt);
	return;
    }
    rt->gcPoke = JS_FALSE;
    METER(rt->gcStats.poke++);

#ifdef JS_THREADSAFE
    /* Bump gcLevel and return rather than nest on this context. */
    if (cx->gcActive) {
	rt->gcLevel++;
	METER(if (rt->gcLevel > rt->gcStats.maxlevel)
		  rt->gcStats.maxlevel = rt->gcLevel);
	if (rt->gcLevel > 1) {
	    JS_UNLOCK_GC(rt);
	    return;
	}
    }

    /* If we're in a request, indicate, temporarily, that we're inactive. */
    if (cx->requestDepth) {
	rt->requestCount--;
	JS_NOTIFY_REQUEST_DONE(rt);
    }

    /* If another thread is already in GC, don't attempt GC; wait instead. */
    if (rt->gcLevel > 0) {
	while (rt->gcLevel > 0)
	    JS_AWAIT_GC_DONE(rt);
	if (cx->requestDepth)
	    rt->requestCount++;
	JS_UNLOCK_GC(rt);
	return;
    }

    /* No other thread is in GC, so indicate that we're now in GC. */
    rt->gcLevel = 1;

    /* Also indicate that GC is active on this context. */
    cx->gcActive = JS_TRUE;

    /* Wait for all other requests to finish. */
    while (rt->requestCount > 0)
	JS_AWAIT_REQUEST_DONE(rt);

#else  /* !JS_THREADSAFE */

    /* Bump gcLevel and return rather than nest; the outer gc will restart. */
    rt->gcLevel++;
    METER(if (rt->gcLevel > rt->gcStats.maxlevel)
	      rt->gcStats.maxlevel = rt->gcLevel);
    if (rt->gcLevel > 1)
	return;

#endif /* !JS_THREADSAFE */

    /* Drop atoms held by the property cache, and clear property weak links. */
    js_FlushPropertyCache(cx);
restart:
    rt->gcNumber++;

    /* Mark phase. */
    JS_HashTableEnumerateEntries(rt->gcRootsHash, gc_root_marker, rt);
    js_MarkAtomState(&rt->atomState, gc_mark);
    iter = NULL;
    while ((acx = js_ContextIterator(rt, &iter)) != NULL) {
	/*
	 * Iterate frame chain and dormant chains. Temporarily tack current
	 * frame onto the head of the dormant list to ease iteration.
	 *
	 * (NOTE: see comment on this whole 'dormant' thing in js_Execute)
	 */
	chain = acx->fp;
	if (chain) {
	    JS_ASSERT(!chain->dormantNext);
	    chain->dormantNext = acx->dormantFrameChain;
	} else {
	    chain = acx->dormantFrameChain;
	}
	for (fp=chain; fp; fp = chain = chain->dormantNext) {
	    sp = fp->sp;
	    if (sp) {
		for (a = acx->stackPool.first.next; a; a = a->next) {
		    begin = a->base;
		    end = a->avail;
		    if (JS_UPTRDIFF(sp, begin) < JS_UPTRDIFF(end, begin))
			end = (jsuword)sp;
		    for (vp = (jsval *)begin; vp < (jsval *)end; vp++) {
			v = *vp;
			if (JSVAL_IS_GCTHING(v))
			    GC_MARK(rt, JSVAL_TO_GCTHING(v), "stack", NULL);
		    }
		    if (end == (jsuword)sp)
			break;
		}
	    }
	    do {
		GC_MARK(rt, fp->scopeChain, "scope chain", NULL);
		GC_MARK(rt, fp->thisp, "this", NULL);
		if (JSVAL_IS_GCTHING(fp->rval))
		    GC_MARK(rt, JSVAL_TO_GCTHING(fp->rval), "rval", NULL);
		if (fp->callobj)
		    GC_MARK(rt, fp->callobj, "call object", NULL);
		if (fp->argsobj)
		    GC_MARK(rt, fp->argsobj, "arguments object", NULL);
		if (fp->script)
		    GC_MARK_SCRIPT(rt, fp->script, NULL);
		if (fp->sharpArray)
		    GC_MARK(rt, fp->sharpArray, "sharp array", NULL);
	    } while ((fp = fp->down) != NULL);
	}
	/* cleanup temporary link */
	if (acx->fp)
	    acx->fp->dormantNext = NULL;
	GC_MARK(rt, acx->globalObject, "global object", NULL);
	GC_MARK(rt, acx->newborn[GCX_OBJECT], "newborn object", NULL);
	GC_MARK(rt, acx->newborn[GCX_STRING], "newborn string", NULL);
	GC_MARK(rt, acx->newborn[GCX_DOUBLE], "newborn double", NULL);
#if JS_HAS_EXCEPTIONS
	if (acx->throwing && JSVAL_IS_GCTHING(acx->exception))
	    GC_MARK(rt, JSVAL_TO_GCTHING(acx->exception), "exception", NULL);
#endif
    }

    /* Sweep phase.  Mark in tempPool for release at label out:. */
    ma = cx->tempPool.current;
    mark = JS_ARENA_MARK(&cx->tempPool);
    js_SweepAtomState(&rt->atomState);
    fa = rt->gcFlagsPool.first.next;
    flagp = (uint8 *)fa->base;
    for (a = rt->gcArenaPool.first.next; a; a = a->next) {
	for (thing = (JSGCThing *)a->base; thing < (JSGCThing *)a->avail;
	     thing++) {
	    if (flagp >= (uint8 *)fa->avail) {
		fa = fa->next;
		JS_ASSERT(fa);
		if (!fa) {
		    METER(rt->gcStats.badflag++);
		    goto out;
		}
		flagp = (uint8 *)fa->base;
	    }
	    flags = *flagp;
	    if (flags & GCF_MARK) {
		*flagp &= ~GCF_MARK;
	    } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) {
		JS_ARENA_ALLOCATE(final, &cx->tempPool, sizeof(JSGCThing));
		if (!final)
		    goto out;
		final->next = thing;
		final->flagp = flagp;
		JS_ASSERT(rt->gcBytes >= sizeof(JSGCThing) + sizeof(uint8));
		rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8);
	    }
	    flagp++;
	}
    }

    /* Finalize phase.  Don't hold the GC lock while running finalizers! */
    JS_UNLOCK_GC(rt);
    for (final = mark; ; final++) {
	if ((jsuword)final >= ma->avail) {
	    ma = ma->next;
	    if (!ma)
		break;
	    final = (JSGCThing *)ma->base;
	}
	thing = final->next;
	flagp = final->flagp;
	flags = *flagp;
	finalizer = gc_finalizers[flags & GCF_TYPEMASK];
	if (finalizer) {
	    *flagp |= GCF_FINAL;
	    finalizer(cx, thing);
	}

	/*
	 * Set flags to GCF_FINAL, signifying that thing is free, but don't
	 * thread thing onto rt->gcFreeList.  We need the GC lock to rebuild
	 * the freelist below while also looking for free-able arenas.
	 */
	*flagp = GCF_FINAL;
    }
    JS_LOCK_GC(rt);

    /* Free unused arenas and rebuild the freelist. */
    ap = &rt->gcArenaPool.first.next;
    a = *ap;
    if (!a)
	goto out;
    thing = (JSGCThing *)a->base;
    a_all_clear = f_all_clear = JS_TRUE;
    flp = oflp = &rt->gcFreeList;
    *flp = NULL;
    METER(rt->gcStats.freelen = 0);

    fap = &rt->gcFlagsPool.first.next;
    while ((fa = *fap) != NULL) {
	/* XXX optimize by unrolling to use word loads */
	for (flagp = (uint8 *)fa->base; ; flagp++) {
	    JS_ASSERT(a);
	    if (!a) {
		METER(rt->gcStats.badarena++);
		goto out;
	    }
	    if (thing >= (JSGCThing *)a->avail) {
		if (a_all_clear) {
		    JS_ARENA_DESTROY(&rt->gcArenaPool, a, ap);
		    flp = oflp;
		    METER(rt->gcStats.afree++);
		} else {
		    ap = &a->next;
		    a_all_clear = JS_TRUE;
		    oflp = flp;
		}
		a = *ap;
		if (!a)
		    break;
		thing = (JSGCThing *)a->base;
	    }
	    if (flagp >= (uint8 *)fa->avail)
		break;
	    if (*flagp != GCF_FINAL) {
		a_all_clear = f_all_clear = JS_FALSE;
	    } else {
		thing->flagp = flagp;
		*flp = thing;
		flp = &thing->next;
		METER(rt->gcStats.freelen++);
	    }
	    thing++;
	}
	if (f_all_clear) {
	    JS_ARENA_DESTROY(&rt->gcFlagsPool, fa, fap);
	    METER(rt->gcStats.fafree++);
	} else {
	    fap = &fa->next;
	    f_all_clear = JS_TRUE;
	}
    }

    /* Terminate the new freelist. */
    *flp = NULL;

out:
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    if (rt->gcLevel > 1) {
	rt->gcLevel = 1;
	goto restart;
    }
    rt->gcLevel = 0;
    rt->gcLastBytes = rt->gcBytes;

#ifdef JS_THREADSAFE
    /* If we were invoked during a request, undo the temporary decrement. */
    if (cx->requestDepth)
	rt->requestCount++;
    cx->gcActive = JS_FALSE;
    JS_NOTIFY_GC_DONE(rt);
    JS_UNLOCK_GC(rt);
#endif
    if (rt->gcCallback)
	(void) rt->gcCallback(cx, JSGC_END);
}

**** End of jsgc.c. ****

**** Start of jsgc.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsgc_h___
#define jsgc_h___
/*
 * JS Garbage Collector.
 */
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

/* GC thing type indexes. */
#define GCX_OBJECT	0			/* JSObject */
#define GCX_STRING	1			/* JSString */
#define GCX_DOUBLE	2			/* jsdouble */
#define GCX_DECIMAL     3                       /* JSDecimal */
#define GCX_NTYPES      4

/* GC flag definitions (type index goes in low bits). */
#define GCF_TYPEMASK	JS_BITMASK(2)		/* use low bits for type */
#define GCF_MARK	JS_BIT(2)		/* mark bit */
#define GCF_FINAL	JS_BIT(3)		/* in finalization bit */
#define GCF_LOCKBIT	4			/* lock bit shift and mask */
#define GCF_LOCKMASK	(JS_BITMASK(4) << GCF_LOCKBIT)
#define GCF_LOCK	JS_BIT(GCF_LOCKBIT)	/* lock request bit in API */

#if 1
/*
 * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles
 * loading oldval.  XXX remove implied force, poke in addroot/removeroot, &c
 */
#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE)
#else
#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval))
#endif

extern JSBool
js_InitGC(JSRuntime *rt, uint32 maxbytes);

extern void
js_FinishGC(JSRuntime *rt);

extern JSBool
js_AddRoot(JSContext *cx, void *rp, const char *name);

extern JSBool
js_RemoveRoot(JSContext *cx, void *rp);

extern void *
js_AllocGCThing(JSContext *cx, uintN flags);

extern JSBool
js_LockGCThing(JSContext *cx, void *thing);

extern JSBool
js_UnlockGCThing(JSContext *cx, void *thing);

extern JS_FRIEND_API(void)
js_ForceGC(JSContext *cx);

extern void
js_GC(JSContext *cx);

#ifdef JS_GCMETER

typedef struct JSGCStats {
    uint32  alloc;      /* number of allocation attempts */
    uint32  freelen;    /* gcFreeList length */
    uint32  recycle;    /* number of things recycled through gcFreeList */
    uint32  retry;      /* allocation attempt retries after running the GC */
    uint32  fail;       /* allocation failures */
    uint32  lock;       /* valid lock calls */
    uint32  unlock;     /* valid unlock calls */
    uint32  stuck;      /* stuck reference counts seen by lock calls */
    uint32  unstuck;    /* unlock calls that saw a stuck lock count */
    uint32  depth;      /* mark recursion depth */
    uint32  maxdepth;   /* maximum mark recursion depth */
    uint32  maxlevel;   /* maximum GC nesting (indirect recursion) level */
    uint32  poke;       /* number of potentially useful GC calls */
    uint32  nopoke;     /* useless GC calls where js_PokeGC was not set */
    uint32  badarena;   /* thing arena corruption */
    uint32  badflag;    /* flags arena corruption */
    uint32  afree;      /* thing arenas freed so far */
    uint32  fafree;     /* flags arenas freed so far */
} JSGCStats;

extern void
js_DumpGCStats(JSRuntime *rt, FILE *fp);

#endif /* JS_GCMETER */

JS_END_EXTERN_C

#endif /* jsgc_h___ */

**** End of jsgc.h. ****

**** Start of jshash.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * PR hash table package.
 */
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsbit.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jshash.h" /* Added by JSIFY */

/* Compute the number of buckets in ht */
#define NBUCKETS(ht)    JS_BIT(JS_HASH_BITS - (ht)->shift)

/* The smallest table has 16 buckets */
#define MINBUCKETSLOG2  4
#define MINBUCKETS      JS_BIT(MINBUCKETSLOG2)

/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
#define OVERLOADED(n)   ((n) - ((n) >> 3))

/* Compute the number of entries below which we shrink the table by half */
#define UNDERLOADED(n)  (((n) > MINBUCKETS) ? ((n) >> 2) : 0)

/*
** Stubs for default hash allocator ops.
*/
static void *
DefaultAllocTable(void *pool, size_t size)
{
    return malloc(size);
}

static void
DefaultFreeTable(void *pool, void *item)
{
    free(item);
}

static JSHashEntry *
DefaultAllocEntry(void *pool, const void *key)
{
    return malloc(sizeof(JSHashEntry));
}

static void
DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag)
{
    if (flag == HT_FREE_ENTRY)
        free(he);
}

static JSHashAllocOps defaultHashAllocOps = {
    DefaultAllocTable, DefaultFreeTable,
    DefaultAllocEntry, DefaultFreeEntry
};

JS_EXPORT_API(JSHashTable *)
JS_NewHashTable(uint32 n, JSHashFunction keyHash,
                JSHashComparator keyCompare, JSHashComparator valueCompare,
                JSHashAllocOps *allocOps, void *allocPriv)
{
    JSHashTable *ht;
    size_t nb;

    if (n <= MINBUCKETS) {
        n = MINBUCKETSLOG2;
    } else {
        n = JS_CeilingLog2(n);
        if ((int32)n < 0)
            return NULL;
    }

    if (!allocOps) allocOps = &defaultHashAllocOps;

    ht = (*allocOps->allocTable)(allocPriv, sizeof *ht);
    if (!ht)
	return NULL;
    memset(ht, 0, sizeof *ht);
    ht->shift = JS_HASH_BITS - n;
    n = JS_BIT(n);
#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800
    if (n > 16000) {
        (*allocOps->freeTable)(allocPriv, ht);
        return NULL;
    }
#endif  /* WIN16 */
    nb = n * sizeof(JSHashEntry *);
    ht->buckets = (*allocOps->allocTable)(allocPriv, nb);
    if (!ht->buckets) {
        (*allocOps->freeTable)(allocPriv, ht);
        return NULL;
    }
    memset(ht->buckets, 0, nb);

    ht->keyHash = keyHash;
    ht->keyCompare = keyCompare;
    ht->valueCompare = valueCompare;
    ht->allocOps = allocOps;
    ht->allocPriv = allocPriv;
    return ht;
}

JS_EXPORT_API(void)
JS_HashTableDestroy(JSHashTable *ht)
{
    uint32 i, n;
    JSHashEntry *he, *next;
    JSHashAllocOps *allocOps = ht->allocOps;
    void *allocPriv = ht->allocPriv;

    n = NBUCKETS(ht);
    for (i = 0; i < n; i++) {
        for (he = ht->buckets[i]; he; he = next) {
            next = he->next;
            (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
        }
    }
#ifdef DEBUG
    memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
#endif
    (*allocOps->freeTable)(allocPriv, ht->buckets);
#ifdef DEBUG
    memset(ht, 0xDB, sizeof *ht);
#endif
    (*allocOps->freeTable)(allocPriv, ht);
}

/*
** Multiplicative hash, from Knuth 6.4.
*/
JS_EXPORT_API(JSHashEntry **)
JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
{
    JSHashEntry *he, **hep, **hep0;
    JSHashNumber h;

#ifdef HASHMETER
    ht->nlookups++;
#endif
    h = keyHash * JS_GOLDEN_RATIO;
    h >>= ht->shift;
    hep = hep0 = &ht->buckets[h];
    while ((he = *hep) != NULL) {
        if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
            /* Move to front of chain if not already there */
            if (hep != hep0) {
                *hep = he->next;
                he->next = *hep0;
                *hep0 = he;
            }
            return hep0;
        }
        hep = &he->next;
#ifdef HASHMETER
        ht->nsteps++;
#endif
    }
    return hep;
}

JS_EXPORT_API(JSHashEntry *)
JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,
                   JSHashNumber keyHash, const void *key, void *value)
{
    uint32 i, n;
    JSHashEntry *he, *next, **oldbuckets;
    size_t nb;

    /* Grow the table if it is overloaded */
    n = NBUCKETS(ht);
    if (ht->nentries >= OVERLOADED(n)) {
#ifdef HASHMETER
        ht->ngrows++;
#endif
        ht->shift--;
        oldbuckets = ht->buckets;
#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800
        if (2 * n > 16000)
            return NULL;
#endif  /* WIN16 */
        nb = 2 * n * sizeof(JSHashEntry *);
        ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb);
        if (!ht->buckets) {
            ht->buckets = oldbuckets;
            return NULL;
	}
        memset(ht->buckets, 0, nb);

        for (i = 0; i < n; i++) {
            for (he = oldbuckets[i]; he; he = next) {
                next = he->next;
                hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);
                JS_ASSERT(*hep == NULL);
                he->next = NULL;
                *hep = he;
            }
        }
#ifdef DEBUG
        memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
#endif
        (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
        hep = JS_HashTableRawLookup(ht, keyHash, key);
    }

    /* Make a new key value entry */
    he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
    if (!he)
	return NULL;
    he->keyHash = keyHash;
    he->key = key;
    he->value = value;
    he->next = *hep;
    *hep = he;
    ht->nentries++;
    return he;
}

JS_EXPORT_API(JSHashEntry *)
JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)
{
    JSHashNumber keyHash;
    JSHashEntry *he, **hep;

    keyHash = (*ht->keyHash)(key);
    hep = JS_HashTableRawLookup(ht, keyHash, key);
    if ((he = *hep) != NULL) {
        /* Hit; see if values match */
        if ((*ht->valueCompare)(he->value, value)) {
            /* key,value pair is already present in table */
            return he;
        }
        if (he->value)
            (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
        he->value = value;
        return he;
    }
    return JS_HashTableRawAdd(ht, hep, keyHash, key, value);
}

JS_EXPORT_API(void)
JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
{
    uint32 i, n;
    JSHashEntry *next, **oldbuckets;
    size_t nb;

    *hep = he->next;
    (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);

    /* Shrink table if it's underloaded */
    n = NBUCKETS(ht);
    if (--ht->nentries < UNDERLOADED(n)) {
#ifdef HASHMETER
        ht->nshrinks++;
#endif
        ht->shift++;
        oldbuckets = ht->buckets;
        nb = n * sizeof(JSHashEntry*) / 2;
        ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb);
        if (!ht->buckets) {
            ht->buckets = oldbuckets;
            return;
        }
        memset(ht->buckets, 0, nb);

        for (i = 0; i < n; i++) {
            for (he = oldbuckets[i]; he; he = next) {
                next = he->next;
                hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);
                JS_ASSERT(*hep == NULL);
                he->next = NULL;
                *hep = he;
            }
        }
#ifdef DEBUG
        memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
#endif
        (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
    }
}

JS_EXPORT_API(JSBool)
JS_HashTableRemove(JSHashTable *ht, const void *key)
{
    JSHashNumber keyHash;
    JSHashEntry *he, **hep;

    keyHash = (*ht->keyHash)(key);
    hep = JS_HashTableRawLookup(ht, keyHash, key);
    if ((he = *hep) == NULL)
        return JS_FALSE;

    /* Hit; remove element */
    JS_HashTableRawRemove(ht, hep, he);
    return JS_TRUE;
}

JS_EXPORT_API(void *)
JS_HashTableLookup(JSHashTable *ht, const void *key)
{
    JSHashNumber keyHash;
    JSHashEntry *he, **hep;

    keyHash = (*ht->keyHash)(key);
    hep = JS_HashTableRawLookup(ht, keyHash, key);
    if ((he = *hep) != NULL) {
        return he->value;
    }
    return NULL;
}

/*
** Iterate over the entries in the hash table calling func for each
** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP).
** Return a count of the number of elements scanned.
*/
JS_EXPORT_API(int)
JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
{
    JSHashEntry *he, **hep;
    uint32 i, nbuckets;
    int rv, n = 0;
    JSHashEntry *todo = NULL;

    nbuckets = NBUCKETS(ht);
    for (i = 0; i < nbuckets; i++) {
        hep = &ht->buckets[i];
        while ((he = *hep) != NULL) {
            rv = (*f)(he, n, arg);
            n++;
            if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
                *hep = he->next;
                if (rv & HT_ENUMERATE_REMOVE) {
                    he->next = todo;
                    todo = he;
                }
            } else {
                hep = &he->next;
            }
            if (rv & HT_ENUMERATE_STOP) {
                goto out;
            }
        }
    }

out:
    hep = &todo;
    while ((he = *hep) != NULL) {
        JS_HashTableRawRemove(ht, hep, he);
    }
    return n;
}

#ifdef HASHMETER
#include <math.h>
#include <stdio.h>

JS_EXPORT_API(void)
JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
{
    double mean, variance;
    uint32 nchains, nbuckets;
    uint32 i, n, maxChain, maxChainLen;
    JSHashEntry *he;

    variance = 0;
    nchains = 0;
    maxChainLen = 0;
    nbuckets = NBUCKETS(ht);
    for (i = 0; i < nbuckets; i++) {
        he = ht->buckets[i];
        if (!he)
            continue;
        nchains++;
        for (n = 0; he; he = he->next)
            n++;
        variance += n * n;
        if (n > maxChainLen) {
            maxChainLen = n;
            maxChain = i;
        }
    }
    mean = (double)ht->nentries / nchains;
    variance = fabs(variance / nchains - mean * mean);

    fprintf(fp, "\nHash table statistics:\n");
    fprintf(fp, "     number of lookups: %u\n", ht->nlookups);
    fprintf(fp, "     number of entries: %u\n", ht->nentries);
    fprintf(fp, "       number of grows: %u\n", ht->ngrows);
    fprintf(fp, "     number of shrinks: %u\n", ht->nshrinks);
    fprintf(fp, "   mean steps per hash: %g\n", (double)ht->nsteps
                                                / ht->nlookups);
    fprintf(fp, "mean hash chain length: %g\n", mean);
    fprintf(fp, "    standard deviation: %g\n", sqrt(variance));
    fprintf(fp, " max hash chain length: %u\n", maxChainLen);
    fprintf(fp, "        max hash chain: [%u]\n", maxChain);

    for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
        if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
            break;
}
#endif /* HASHMETER */

JS_EXPORT_API(int)
JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
{
    int count;

    count = JS_HashTableEnumerateEntries(ht, dump, fp);
#ifdef HASHMETER
    JS_HashTableDumpMeter(ht, dump, fp);
#endif
    return count;
}

JS_EXPORT_API(JSHashNumber)
JS_HashString(const void *key)
{
    JSHashNumber h;
    const unsigned char *s;

    h = 0;
    for (s = key; *s; s++)
        h = (h >> 28) ^ (h << 4) ^ *s;
    return h;
}

JS_EXPORT_API(int)
JS_CompareValues(const void *v1, const void *v2)
{
    return v1 == v2;
}

**** End of jshash.c. ****

**** Start of jshash.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jshash_h___
#define jshash_h___
/*
 * API to portable hash table code.
 */
#include <stddef.h>
#include <stdio.h>
#include "jstypes.h"
#include "jscompat.h"

JS_BEGIN_EXTERN_C

typedef uint32 JSHashNumber;
typedef struct JSHashEntry JSHashEntry;
typedef struct JSHashTable JSHashTable;

#define JS_HASH_BITS 32
#define JS_GOLDEN_RATIO 0x9E3779B9U

typedef JSHashNumber (*JSHashFunction)(const void *key);
typedef intN (*JSHashComparator)(const void *v1, const void *v2);
typedef intN (*JSHashEnumerator)(JSHashEntry *he, intN i, void *arg);

/* Flag bits in JSHashEnumerator's return value */
#define HT_ENUMERATE_NEXT       0       /* continue enumerating entries */
#define HT_ENUMERATE_STOP       1       /* stop enumerating entries */
#define HT_ENUMERATE_REMOVE     2       /* remove and free the current entry */
#define HT_ENUMERATE_UNHASH     4       /* just unhash the current entry */

typedef struct JSHashAllocOps {
    void *              (*allocTable)(void *pool, size_t size);
    void                (*freeTable)(void *pool, void *item);
    JSHashEntry *       (*allocEntry)(void *pool, const void *key);
    void                (*freeEntry)(void *pool, JSHashEntry *he, uintN flag);
} JSHashAllocOps;

#define HT_FREE_VALUE   0               /* just free the entry's value */
#define HT_FREE_ENTRY   1               /* free value and entire entry */

struct JSHashEntry {
    JSHashEntry         *next;          /* hash chain linkage */
    JSHashNumber          keyHash;        /* key hash function result */
    const void          *key;           /* ptr to opaque key */
    void                *value;         /* ptr to opaque value */
};

struct JSHashTable {
    JSHashEntry         **buckets;      /* vector of hash buckets */
    uint32              nentries;       /* number of entries in table */
    uint32              shift;          /* multiplicative hash shift */
    JSHashFunction      keyHash;        /* key hash function */
    JSHashComparator    keyCompare;     /* key comparison function */
    JSHashComparator    valueCompare;   /* value comparison function */
    JSHashAllocOps      *allocOps;      /* allocation operations */
    void                *allocPriv;     /* allocation private data */
#ifdef HASHMETER
    uint32              nlookups;       /* total number of lookups */
    uint32              nsteps;         /* number of hash chains traversed */
    uint32              ngrows;         /* number of table expansions */
    uint32              nshrinks;       /* number of table contractions */
#endif
};

/*
 * Create a new hash table.
 * If allocOps is null, use default allocator ops built on top of malloc().
 */
JS_EXTERN_API(JSHashTable *)
JS_NewHashTable(uint32 n, JSHashFunction keyHash,
                JSHashComparator keyCompare, JSHashComparator valueCompare,
                JSHashAllocOps *allocOps, void *allocPriv);

JS_EXTERN_API(void)
JS_HashTableDestroy(JSHashTable *ht);

/* Low level access methods */
JS_EXTERN_API(JSHashEntry **)
JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key);

JS_EXTERN_API(JSHashEntry *)
JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash,
                   const void *key, void *value);

JS_EXTERN_API(void)
JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he);

/* Higher level access methods */
JS_EXTERN_API(JSHashEntry *)
JS_HashTableAdd(JSHashTable *ht, const void *key, void *value);

JS_EXTERN_API(JSBool)
JS_HashTableRemove(JSHashTable *ht, const void *key);

JS_EXTERN_API(intN)
JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg);

JS_EXTERN_API(void *)
JS_HashTableLookup(JSHashTable *ht, const void *key);

JS_EXTERN_API(intN)
JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp);

/* General-purpose C string hash function. */
JS_EXTERN_API(JSHashNumber)
JS_HashString(const void *key);

/* Stub function just returns v1 == v2 */
JS_EXTERN_API(intN)
JS_CompareValues(const void *v1, const void *v2);

JS_END_EXTERN_C

#endif /* jshash_h___ */

**** End of jshash.h. ****

**** Start of jsinterp.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JavaScript bytecode interpreter.
 */
#include "jsstddef.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

void
js_FlushPropertyCache(JSContext *cx)
{
    JSPropertyCache *cache;

    cache = &cx->runtime->propertyCache;
    if (cache->empty)
	return;
    memset(cache->table, 0, sizeof cache->table);
    cache->empty = JS_TRUE;
    cache->flushes++;
}

void
js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop)
{
    JSPropertyCache *cache;
    JSBool empty;
    JSPropertyCacheEntry *end, *pce, entry;
    JSProperty *pce_prop;

    cache = &cx->runtime->propertyCache;
    if (cache->empty)
	return;

    empty = JS_TRUE;
    end = &cache->table[PROPERTY_CACHE_SIZE];
    for (pce = &cache->table[0]; pce < end; pce++) {
	PCE_LOAD(cache, pce, entry);
	pce_prop = PCE_PROPERTY(entry);
	if (pce_prop) {
	    if (pce_prop == prop) {
		PCE_OBJECT(entry) = NULL;
		PCE_PROPERTY(entry) = NULL;
		PCE_STORE(cache, pce, entry);
	    } else {
		empty = JS_FALSE;
	    }
	}
    }
    cache->empty = empty;
}

/*
 * Class for for/in loop property iterator objects.
 */

static void prop_iterator_finalize(JSContext *cx, JSObject *obj);

static JSClass prop_iterator_class = {
    "PropertyIterator",
#if 1 //SRJMOD
    JSCLASS_HAS_PRIVATE,
#else
    0,
#endif
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   prop_iterator_finalize
};

#if 1 //SRJMOD
#define JSSLOT_ITR_STATE    (JSSLOT_FREE(&prop_iterator_class))
#else
#define JSSLOT_ITR_STATE    (JSSLOT_START)
#endif

#if XP_MAC //SRJMOD
static EnumDestroyerProcPtr FwEnumDestroyer;

void js_SetFwEnumDestroyer(EnumDestroyerProcPtr destroyer)
{
	FwEnumDestroyer = destroyer;
}
#else
extern JSBool FwEnumDestroyer(jsval iter_state);
#endif

static void prop_iterator_finalize(JSContext *cx, JSObject *obj)
{
    jsval iter_state;

#if 1 //SRJMOD
    JS_ASSERT(JSSLOT_ITR_STATE != JSSLOT_PRIVATE);
#endif

    /* Protect against stillborn iterators. */
    iter_state = obj->slots[JSSLOT_ITR_STATE];
    if (iter_state != JSVAL_NULL) {
#if 1 //SRJMOD
    	jsval private = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
	if (private != JSVAL_NULL) {
            long classflags = (long)JSVAL_TO_PRIVATE(private);
            if (classflags & JSCLASS_FW_ENUM) {
            	// destroy it a special way
#if XP_MAC
				if((*FwEnumDestroyer)(iter_state))
#else
            	if (FwEnumDestroyer(iter_state))
#endif
            		return;
            }
	}
#endif
	OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, NULL);
    }
}

/*
 * Stack macros and functions.  These all use a local variable, jsval *sp, to
 * point to the next free stack slot.  SAVE_SP must be called before any call
 * to a function that may invoke the interpreter.  RESTORE_SP must be called
 * only after return from js_Invoke, because only js_Invoke changes fp->sp.
 */
#define PUSH(v)         (*sp++ = (v))
#define POP()           (*--sp)
#define SAVE_SP(fp)     ((fp)->sp = sp)
#define RESTORE_SP(fp)  (sp = (fp)->sp)

/*
 * Push the generating bytecode's pc onto the parallel pc stack that runs
 * depth slots below the operands.
 *
 * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment.  See
 * Interpret for these local variables' declarations and uses.
 */
#define PUSH_OPND(v)    (sp[-depth] = (jsval)pc, PUSH(v))

/*
 * Push the jsdouble d using sp, depth, and pc from the lexical environment.
 * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space
 * for it and push a reference.
 */
#define PUSH_NUMBER(cx, d)                                                    \
    JS_BEGIN_MACRO                                                            \
	jsint _i;                                                             \
	jsval _v;                                                             \
									      \
	if (JSDOUBLE_IS_INT(d, _i) && INT_FITS_IN_JSVAL(_i)) {                \
	    _v = INT_TO_JSVAL(_i);                                            \
	} else {                                                              \
	    ok = js_NewDoubleValue(cx, d, &_v);                               \
	    if (!ok)                                                          \
		goto out;                                                     \
	}                                                                     \
	PUSH_OPND(_v);                                                        \
    JS_END_MACRO

#define POP_NUMBER(cx, d)                                                     \
    JS_BEGIN_MACRO                                                            \
	jsval _v;                                                             \
									      \
	_v = POP();                                                           \
	VALUE_TO_NUMBER(cx, _v, d);                                           \
    JS_END_MACRO

/*
 * This POP variant is called only for bitwise operators, so we don't bother
 * to inline it.  The calls in Interpret must therefore SAVE_SP first!
 */
static JSBool
PopInt(JSContext *cx, jsint *ip)
{
    JSStackFrame *fp;
    jsval *sp, v;

    fp = cx->fp;
    RESTORE_SP(fp);
    v = POP();
    SAVE_SP(fp);
    if (JSVAL_IS_INT(v)) {
	*ip = JSVAL_TO_INT(v);
	return JS_TRUE;
    }
    return js_ValueToECMAInt32(cx, v, (int32 *)ip);
}

static JSBool
PopUint(JSContext *cx, jsuint *ip)
{
    JSStackFrame *fp;
    jsval *sp, v;
    jsint i;

    fp = cx->fp;
    RESTORE_SP(fp);
    v = POP();
    SAVE_SP(fp);
    if (JSVAL_IS_INT(v) && (i = JSVAL_TO_INT(v)) >= 0) {
	*ip = i;
	return JS_TRUE;
    }
    return js_ValueToECMAUint32(cx, v, (uint32 *)ip);
}

/*
 * Optimized conversion macros that test for the desired type in v before
 * homing sp and calling a conversion function.
 */
#define VALUE_TO_NUMBER(cx, v, d)                                             \
    JS_BEGIN_MACRO                                                            \
	if (JSVAL_IS_INT(v)) {                                                \
	    d = (jsdouble)JSVAL_TO_INT(v);                                    \
	} else if (JSVAL_IS_DOUBLE(v)) {                                      \
	    d = *JSVAL_TO_DOUBLE(v);                                          \
	} else {                                                              \
	    SAVE_SP(fp);                                                      \
	    ok = js_ValueToNumber(cx, v, &d);                                 \
	    if (!ok)                                                          \
		goto out;                                                     \
	}                                                                     \
    JS_END_MACRO

#define POP_BOOLEAN(cx, v, b)                                                 \
    JS_BEGIN_MACRO                                                            \
	v = POP();                                                            \
	if (v == JSVAL_NULL) {                                                \
	    b = JS_FALSE;                                                     \
	} else if (JSVAL_IS_BOOLEAN(v)) {                                     \
	    b = JSVAL_TO_BOOLEAN(v);                                          \
	} else {                                                              \
	    SAVE_SP(fp);                                                      \
	    ok = js_ValueToBoolean(cx, v, &b);                                \
	    if (!ok)                                                          \
		goto out;                                                     \
	}                                                                     \
    JS_END_MACRO

#define VALUE_TO_OBJECT(cx, v, obj)                                           \
    JS_BEGIN_MACRO                                                            \
	if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) {                          \
	    obj = JSVAL_TO_OBJECT(v);                                         \
	} else {                                                              \
	    SAVE_SP(fp);                                                      \
	    obj = js_ValueToNonNullObject(cx, v);                             \
	    if (!obj) {                                                       \
		ok = JS_FALSE;                                                \
		goto out;                                                     \
	    }                                                                 \
	}                                                                     \
    JS_END_MACRO

#if JS_BUG_VOID_TOSTRING
#define CHECK_VOID_TOSTRING(cx, v)                                            \
    if (JSVAL_IS_VOID(v)) {                                                   \
	JSString *_str;                                                       \
	_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \
	v = STRING_TO_JSVAL(_str);                                            \
    }
#else
#define CHECK_VOID_TOSTRING(cx, v)  ((void)0)
#endif

#if JS_BUG_EAGER_TOSTRING
#define CHECK_EAGER_TOSTRING(hint)  (hint = JSTYPE_STRING)
#else
#define CHECK_EAGER_TOSTRING(hint)  ((void)0)
#endif

#define VALUE_TO_PRIMITIVE(cx, v, hint, vp)                                   \
    JS_BEGIN_MACRO                                                            \
	if (JSVAL_IS_PRIMITIVE(v)) {                                          \
	    CHECK_VOID_TOSTRING(cx, v);                                       \
	    *vp = v;                                                          \
	} else {                                                              \
	    SAVE_SP(fp);                                                      \
	    CHECK_EAGER_TOSTRING(hint);                                       \
	    ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp);         \
	    if (!ok)                                                          \
		goto out;                                                     \
	}                                                                     \
    JS_END_MACRO

jsval *
js_AllocStack(JSContext *cx, uintN nslots, void **markp)
{
    jsval *sp;

    if (markp)
	*markp = JS_ARENA_MARK(&cx->stackPool);
    JS_ARENA_ALLOCATE(sp, &cx->stackPool, nslots * sizeof(jsval));
    if (!sp) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW,
			     (cx->fp && cx->fp->fun)
			     ? JS_GetFunctionName(cx->fp->fun)
			     : "script");
    }
    return sp;
}

void
js_FreeStack(JSContext *cx, void *mark)
{
    JS_ARENA_RELEASE(&cx->stackPool, mark);
}

JSBool
js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFunction *fun;
    JSStackFrame *fp;

    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);
    fun = JS_GetPrivate(cx, obj);
    for (fp = cx->fp; fp; fp = fp->down) {
	/* Find most recent non-native function frame. */
	if (fp->fun && !fp->fun->call) {
	    if (fp->fun == fun) {
		JS_ASSERT((uintN)JSVAL_TO_INT(id) < fp->fun->nargs);
		*vp = fp->argv[JSVAL_TO_INT(id)];
	    }
	    return JS_TRUE;
	}
    }
    return JS_TRUE;
}

JSBool
js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFunction *fun;
    JSStackFrame *fp;

    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);
    fun = JS_GetPrivate(cx, obj);
    for (fp = cx->fp; fp; fp = fp->down) {
	/* Find most recent non-native function frame. */
	if (fp->fun && !fp->fun->call) {
	    if (fp->fun == fun) {
		JS_ASSERT((uintN)JSVAL_TO_INT(id) < fp->fun->nargs);
		fp->argv[JSVAL_TO_INT(id)] = *vp;
	    }
	    return JS_TRUE;
	}
    }
    return JS_TRUE;
}

JSBool
js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFunction *fun;
    JSStackFrame *fp;
    jsint slot;

    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);
    fun = JS_GetPrivate(cx, obj);
    for (fp = cx->fp; fp; fp = fp->down) {
	/* Find most recent non-native function frame. */
	if (fp->fun && !fp->fun->call) {
	    if (fp->fun == fun) {
		slot = JSVAL_TO_INT(id);
		JS_ASSERT((uintN)slot < fp->fun->nvars);
		if ((uintN)slot < fp->nvars)
		    *vp = fp->vars[slot];
	    }
	    return JS_TRUE;
	}
    }
    return JS_TRUE;
}

JSBool
js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSFunction *fun;
    JSStackFrame *fp;
    jsint slot;

    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);
    fun = JS_GetPrivate(cx, obj);
    for (fp = cx->fp; fp; fp = fp->down) {
	/* Find most recent non-native function frame. */
	if (fp->fun && !fp->fun->call) {
	    if (fp->fun == fun) {
		slot = JSVAL_TO_INT(id);
		JS_ASSERT((uintN)slot < fp->fun->nvars);
		if ((uintN)slot < fp->nvars)
		    fp->vars[slot] = *vp;
	    }
	    return JS_TRUE;
	}
    }
    return JS_TRUE;

}


/*
 * Find a function reference and its 'this' object implicit first parameter
 * under argc arguments on cx's stack, and call the function.  Push missing
 * required arguments, allocate declared local variables, and pop everything
 * when done.  Then push the return value.
 */
JSBool
js_Invoke(JSContext *cx, uintN argc, JSBool constructing)
{
    JSStackFrame *fp, frame;
    jsval *sp, *newsp;
    jsval *vp, v;
    JSObject *funobj, *parent, *thisp;
    JSClass *clasp;
    JSObjectOps *ops;
    JSNative native;
    JSFunction *fun;
    JSScript *script;
    uintN minargs, nvars;
    void *mark;
    intN nslots, nalloc, surplus;
    JSBool ok;
    JSInterpreterHook hook;
    void *hookData;

    /* Reach under args and this to find the callable on the stack. */
    fp = cx->fp;
    sp = fp->sp;

    /*
     * Set vp to the callable value's stack slot (it's where rval goes).
     * Once vp is set, control must flow through label out2: to return.
     * Set frame.rval early so native class and object ops can throw and
     * return false, causing a goto out2 with ok set to false.
     */
    vp = sp - (2 + argc);
    v = *vp;
    frame.rval = JSVAL_VOID;

    /* A callable must be an object reference. */
    if (JSVAL_IS_PRIMITIVE(v))
	goto bad;
    funobj = JSVAL_TO_OBJECT(v);

    /* Load callable parent and this parameter for later. */
    parent = OBJ_GET_PARENT(cx, funobj);
    thisp = JSVAL_TO_OBJECT(vp[1]);

    clasp = OBJ_GET_CLASS(cx, funobj);
    if (clasp != &js_FunctionClass) {
	/* Function is inlined, all other classes use object ops. */
	ops = funobj->map->ops;

        /* Try converting to function, for closure and API compatibility. */
        /*
        *   We attempt the conversion under all circumstances for 1.2, but
        *   only if there is a call op defined otherwise. 
        */
        if ((cx->version == JSVERSION_1_2)
            || ((ops == &js_ObjectOps) ? 
                    (clasp->call != 0) : (ops->call != 0)) ) {
	    ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
            if (!ok)
	        goto out2;
        }

	if (!JSVAL_IS_FUNCTION(cx, v)) {
	    fun = NULL;
	    script = NULL;
	    minargs = nvars = 0;
	} else {
	    funobj = JSVAL_TO_OBJECT(v);
	    parent = OBJ_GET_PARENT(cx, funobj);

	    fun = JS_GetPrivate(cx, funobj);
	    if (clasp != &js_ClosureClass) {
		/* Make vp refer to funobj to keep it available as argv[-2]. */
		*vp = v;
		goto have_fun;
	    }

	    /* Closure invocation may need extra arg and local var slots. */
	    script = fun->script;
	    minargs = fun->nargs + fun->extra;
	    nvars = fun->nvars;
	}

	/* Try a call or construct native object op, using fun as fallback. */
	native = constructing ? ops->construct : ops->call;
	if (!native)
	    goto bad;
    } else {
	/* Get private data and set derived locals from it. */
	fun = JS_GetPrivate(cx, funobj);
have_fun:
	native = fun->call;
	script = fun->script;
	minargs = fun->nargs + fun->extra;
	nvars = fun->nvars;

	/* Handle bound method and dynamic scope special cases. */
	if (fun->flags) {
	    if (fun->flags & JSFUN_BOUND_METHOD)
		thisp = parent;
	    else
		parent = NULL;
	}
    }

    /* Initialize most of frame, except for thisp and scopeChain. */
    frame.callobj = frame.argsobj = NULL;
    frame.script = script;
    frame.fun = fun;
    frame.argc = argc;
    frame.argv = sp - argc;
    frame.nvars = nvars;
    frame.vars = sp;
    frame.down = fp;
    frame.annotation = NULL;
    frame.pc = NULL;
    frame.sp = sp;
    frame.sharpDepth = 0;
    frame.sharpArray = NULL;
    frame.constructing = constructing;
    frame.overrides = 0;
    frame.debugging = JS_FALSE;
    frame.dormantNext = NULL;

    /*
     * Compute the 'this' parameter and store it in frame.
     * Activation objects ("Call" objects not created with "new Call", i.e.,
     * "Call" objects with private data) may not be referred to by 'this'
     * as dictated by ECMA.
     */
    if (thisp && !(OBJ_GET_CLASS(cx, thisp) == &js_CallClass &&
		   JS_GetPrivate(cx, thisp) != NULL))
    {
	/* Some objects (e.g., With) delegate 'this' to another object. */
	thisp = OBJ_THIS_OBJECT(cx, thisp);
	if (!thisp) {
	    ok = JS_FALSE;
	    goto out2;
	}

	/* Default return value for a constructor is the new object. */
	if (constructing)
	    frame.rval = OBJECT_TO_JSVAL(thisp);
    } else {
	/*
	 * ECMA requires "the global object", but in the presence of multiple
	 * top-level objects (windows, frames, or certain layers in the client
	 * object model), we prefer fun's parent.  An example that causes this
	 * code to run:
	 *
	 *   // in window w1
	 *   function f() { return this }
	 *   function g() { return f }
	 *
	 *   // in window w2
	 *   var h = w1.g()
	 *   alert(h() == w1)
	 *
	 * The alert should display "true".
	 */
	JS_ASSERT(!constructing);
	if (parent == NULL) {
	    parent = cx->globalObject;
	} else {
	    /* walk up to find the top-level object */
	    JSObject *tmp;
	    thisp = parent;
	    while ((tmp = OBJ_GET_PARENT(cx, thisp)) != NULL)
		thisp = tmp;
	}
    }
    frame.thisp = thisp;

    /* From here on, control must flow through label out: to return. */
    cx->fp = &frame;
    mark = JS_ARENA_MARK(&cx->stackPool);
    /* init these now in case we goto out before first hook call */
    hook = cx->runtime->callHook;
    hookData = NULL;

    /* Check for missing arguments expected by the function. */
    nslots = (intN)((argc < minargs) ? minargs - argc : 0);
    if (nslots) {
	/* All arguments must be contiguous, so we may have to copy actuals. */
	nalloc = nslots;
	if ((jsuword)(sp + nslots) > cx->stackPool.current->limit)
	    nalloc += argc;

	/* Take advantage of the surplus slots in the caller's frame depth. */
	surplus = (jsval *)mark - sp;
	JS_ASSERT(surplus >= 0);
	nalloc -= surplus;

	/* Check whether we have enough space in the caller's frame. */
	if (nalloc > 0) {
	    /* Need space for actuals plus missing formals minus surplus. */
	    newsp = js_AllocStack(cx, (uintN)nalloc, NULL);
	    if (!newsp) {
		ok = JS_FALSE;
		goto out;
	    }

	    /* If we couldn't allocate contiguous args, copy actuals now. */
	    if (newsp != mark) {
		if (argc)
		    memcpy(newsp, frame.argv, argc * sizeof(jsval));
		frame.argv = newsp;
		frame.vars = frame.sp = newsp + argc;
		RESTORE_SP(&frame);
	    }
	}

	/* Advance frame.vars to make room for the missing args. */
	frame.vars += nslots;

	/* Push void to initialize missing args. */
	while (--nslots >= 0)
	    PUSH(JSVAL_VOID);
    }

    /* Now allocate stack space for local variables. */
    nslots = (intN)frame.nvars;
    if (nslots) {
	surplus = (intN)((jsval *)cx->stackPool.current->avail - frame.vars);
	if (surplus < nslots) {
	    newsp = js_AllocStack(cx, (uintN)nslots, NULL);
	    if (!newsp) {
		ok = JS_FALSE;
		goto out;
	    }
	    if (newsp != sp) {
		/* NB: Discontinuity between argv and vars. */
		frame.vars = frame.sp = newsp;
		RESTORE_SP(&frame);
	    }
	}

	/* Push void to initialize local variables. */
	while (--nslots >= 0)
	    PUSH(JSVAL_VOID);
    }

    /* Store the current sp in frame before calling fun. */
    SAVE_SP(&frame);

    /* call the hook if present */
    if (hook && (native || script))
        hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData);

    /* Call the function, either a native method or an interpreted script. */
    if (native) {
	frame.scopeChain = fp->scopeChain;
	ok = native(cx, thisp, argc, frame.argv, &frame.rval);
    } else if (script) {
	frame.scopeChain = funobj;
	if (!parent) {
#if JS_HAS_CALL_OBJECT
	    /* Scope with a call object parented by cx's global object. */
	    funobj = js_GetCallObject(cx, &frame, cx->globalObject, NULL);
	    if (!funobj) {
		ok = JS_FALSE;
		goto out;
	    }
#else
	    /* Bad old code slams globalObject directly into funobj. */
	    OBJ_SET_PARENT(funobj, cx->globalObject);
#endif
	}
	ok = js_Interpret(cx, &v);
#if !JS_HAS_CALL_OBJECT
	if (!parent)
	    OBJ_SET_PARENT(funobj, NULL);
#endif
    } else {
	/* fun might be onerror trying to report a syntax error in itself. */
	frame.scopeChain = NULL;
	ok = JS_TRUE;
    }

out:
    if (hook && hookData)
        hook(cx, &frame, JS_FALSE, &ok, hookData);
#if JS_HAS_CALL_OBJECT
    /* If frame has a call object, sync values and clear back-pointer. */
    if (frame.callobj)
	ok &= js_PutCallObject(cx, &frame);
#endif
#if JS_HAS_ARGS_OBJECT
    /* If frame has an arguments object, sync values clear back-pointer. */
    if (frame.argsobj)
	ok &= js_PutArgsObject(cx, &frame);
#endif

    /* Pop everything off the stack and restore cx->fp. */
    JS_ARENA_RELEASE(&cx->stackPool, mark);
    cx->fp = fp;

out2:
    /* Store the return value and restore sp just above it. */
    *vp = frame.rval;
    fp->sp = vp + 1;
    return ok;

bad:
    js_ReportIsNotFunction(cx, vp, constructing);
    ok = JS_FALSE;
    goto out2;
}

JSBool
js_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval,
		     uintN argc, jsval *argv, jsval *rval)
{
    JSStackFrame *fp, *oldfp, frame;
    jsval *oldsp, *sp;
    void *mark;
    uintN i;
    JSBool ok;

    fp = oldfp = cx->fp;
    if (!fp) {
	memset(&frame, 0, sizeof frame);
	cx->fp = fp = &frame;
    }
    oldsp = fp->sp;
    sp = js_AllocStack(cx, 2 + argc, &mark);
    if (!sp) {
	ok = JS_FALSE;
	goto out;
    }
    fp->sp = sp;

    PUSH(fval);
    PUSH(OBJECT_TO_JSVAL(obj));
    for (i = 0; i < argc; i++)
	PUSH(argv[i]);
    SAVE_SP(fp);
    ok = js_Invoke(cx, argc, JS_FALSE);
    if (ok) {
	RESTORE_SP(fp);
	*rval = POP();
    }

    js_FreeStack(cx, mark);
out:
    fp->sp = oldsp;
    if (oldfp != fp)
	cx->fp = oldfp;

    return ok;
}

JSBool
js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun,
	   JSStackFrame *down, JSBool debugging, jsval *result)
{
    JSStackFrame *oldfp, frame;
    JSBool ok;
    JSInterpreterHook hook;
    void *hookData;

    hook = cx->runtime->executeHook;
    hookData = NULL;
    oldfp = cx->fp;
    frame.callobj = frame.argsobj = NULL;
    frame.script = script;
    frame.fun = fun;
    if (down) {
	/* Propagate arg/var state for eval and the debugger API. */
	frame.thisp = down->thisp;
	frame.argc = down->argc;
	frame.argv = down->argv;
	frame.nvars = down->nvars;
	frame.vars = down->vars;
	frame.annotation = down->annotation;
	frame.sharpArray = down->sharpArray;
    } else {
	frame.thisp = chain;
	frame.argc = frame.nvars = 0;
	frame.argv = frame.vars = NULL;
	frame.annotation = NULL;
	frame.sharpArray = NULL;
    }
    frame.rval = JSVAL_VOID;
    frame.down = down;
    frame.scopeChain = chain;
    frame.pc = NULL;
    frame.sp = oldfp ? oldfp->sp : NULL;
    frame.sharpDepth = 0;
    frame.constructing = JS_FALSE;
    frame.overrides = 0;
    frame.debugging = debugging;
    frame.dormantNext = NULL;

    /*
     * Here we wrap the call to js_Interpret with code to (conditionally)
     * save and restore the old stack frame chain into a chain of 'dormant'
     * frame chains. Since we are replacing cx->fp we were running into the
     * problem that if gc was called, then some of the objects associated
     * with the old frame chain (stored here in the C stack as 'oldfp') were
     * not rooted and were being collected. This was bad. So, now we
     * preserve the links to these 'dormant' frame chains in cx before
     * calling js_Interpret and cleanup afterwards. gc walks these dormant
     * chains and marks objects in the same way that it marks object in the
     * primary cx->fp chain.
     */

    if (oldfp && oldfp != down) {
	JS_ASSERT(!oldfp->dormantNext);
	oldfp->dormantNext = cx->dormantFrameChain;
	cx->dormantFrameChain = oldfp;
    }

    cx->fp = &frame;
    if (hook)
        hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData);

    ok = js_Interpret(cx, result);

    if (hook && hookData)
        hook(cx, &frame, JS_FALSE, &ok, hookData);
    cx->fp = oldfp;

    if (oldfp && oldfp != down) {
	JS_ASSERT(cx->dormantFrameChain == oldfp);
	cx->dormantFrameChain = oldfp->dormantNext;
	oldfp->dormantNext = NULL;
    }

    return ok;
}

#if JS_HAS_EXPORT_IMPORT
/*
 * If id is JSVAL_VOID, import all exported properties from obj.
 */
static JSBool
ImportProperty(JSContext *cx, JSObject *obj, jsid id)
{
    JSBool ok;
    JSIdArray *ida;
    JSProperty *prop;
    JSObject *obj2, *target, *funobj, *closure;
    JSString *str;
    uintN attrs;
    jsint i;
    JSFunction *fun;
    jsval value;

    if (JSVAL_IS_VOID(id)) {
	ida = JS_Enumerate(cx, obj);
	if (!ida)
	    return JS_FALSE;
	ok = JS_TRUE;
	if (ida->length == 0)
	    goto out;
    } else {
	ida = NULL;
	if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
	    return JS_FALSE;
	if (!prop) {
	    str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL);
	    if (str)
		js_ReportIsNotDefined(cx, JS_GetStringBytes(str));
	    return JS_FALSE;
	}
	ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
	OBJ_DROP_PROPERTY(cx, obj2, prop);
	if (!ok)
	    return JS_FALSE;
	if (!(attrs & JSPROP_EXPORTED)) {
	    str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL);
	    if (str) {
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_NOT_EXPORTED,
				     JS_GetStringBytes(str));
	    }
	    return JS_FALSE;
	}
    }

    target = js_FindVariableScope(cx, &fun);
    if (!target) {
	ok = JS_FALSE;
	goto out;
    }

    i = 0;
    do {
	if (ida) {
	    id = ida->vector[i];
	    ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
	    if (!ok)
		goto out;
	    if (!(attrs & JSPROP_EXPORTED))
		continue;
	}
	ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs);
	if (!ok)
	    goto out;
	if (JSVAL_IS_FUNCTION(cx, value)) {
	    funobj = JSVAL_TO_OBJECT(value);
	    closure = js_ConstructObject(cx, &js_ClosureClass, funobj, obj);
	    if (!closure) {
		ok = JS_FALSE;
		goto out;
	    }
	    /*
	     * The Closure() constructor resets the closure object's parent
	     * to be the current scope chain.  Set it to the object that the
	     * imported function is being defined in.
	     */
	    OBJ_SET_PARENT(cx, closure, obj);
	    value = OBJECT_TO_JSVAL(closure);
	}

	/*
	 * Handle the case of importing a property that refers to a local
	 * variable or formal parameter of a function activation.  Those
	 * properties are accessed by opcodes using stack slot numbers
	 * generated by the compiler rather than runtime name-lookup. These
	 * local references, therefore, bypass the normal scope chain lookup.
	 * So, instead of defining a new property in the activation object,
	 * modify the existing value in the stack slot.
	 */
	if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
	    ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
	    if (!ok)
		goto out;
	} else {
	    prop = NULL;
	}
	if (prop && (target == obj2)) {
	    ok = OBJ_SET_PROPERTY(cx, target, id, &value);
	} else {
	    ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
				     attrs & ~JSPROP_EXPORTED,
				     NULL);
	}
	if (prop)
	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	if (!ok)
	    goto out;
    } while (ida && ++i < ida->length);

out:
    if (ida)
	JS_DestroyIdArray(cx, ida);
    return ok;
}
#endif /* JS_HAS_EXPORT_IMPORT */

#if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800
#define MAX_INTERP_LEVEL 1000
#else
#define MAX_INTERP_LEVEL 30
#endif

JSBool
js_Interpret(JSContext *cx, jsval *result)
{
    JSRuntime *rt;
    JSStackFrame *fp;
    JSScript *script;
    JSObject *obj, *obj2, *proto, *parent;
    JSBranchCallback onbranch;
    JSBool ok, cond;
    ptrdiff_t depth, len;
    jsval *sp, *newsp;
    void *mark;
    jsbytecode *pc, *pc2, *endpc;
    JSOp op, op2;
    JSCodeSpec *cs;
    JSAtom *atom;
    uintN argc, slot;
    jsval *vp, lval, rval, ltmp, rtmp;
    jsid id;
    JSObject *withobj, *origobj, *propobj;
    JSIdArray *ida;
    jsval iter_state;
    JSProperty *prop;
    JSScopeProperty *sprop;
    JSString *str, *str2, *str3;
    size_t length, length2, length3;
    jschar *chars;
    jsint i, j;
    jsdouble d, d2;
    JSClass *clasp;
    JSFunction *fun;
    JSType type;
#ifdef DEBUG
    FILE *tracefp;
#endif
#if JS_HAS_SWITCH_STATEMENT
    jsint low, high;
    uintN off, npairs;
    JSBool match;
#endif
#if JS_HAS_LEXICAL_CLOSURE
    JSFunction *fun2;
    JSObject *closure;
#endif
#if JS_HAS_EXPORT_IMPORT
    uintN attrs;
#endif
#if JS_HAS_EXCEPTIONS
    JSTryNote *tn;
    ptrdiff_t offset;
#endif

    if (cx->interpLevel == MAX_INTERP_LEVEL) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
	return JS_FALSE;
    }
    cx->interpLevel++;
    *result = JSVAL_VOID;
    rt = cx->runtime;

    /*
     * Set registerized frame pointer and derived pointers.
     */
    fp = cx->fp;
    script = fp->script;
    obj = fp->scopeChain;

    /*
     * Prepare to call a user-supplied branch handler, and abort the script
     * if it returns false.
     */
    onbranch = cx->branchCallback;
    ok = JS_TRUE;
#define CHECK_BRANCH(len) {                                                   \
    if (len < 0 && onbranch) {                                                \
	SAVE_SP(fp);                                                          \
	if (!(ok = (*onbranch)(cx, script)))                                  \
	    goto out;                                                         \
    }                                                                         \
}

    pc = script->code;
    endpc = pc + script->length;
    len = -1;

    /*
     * Allocate operand and pc stack slots for the script's worst-case depth.
     */
    depth = (ptrdiff_t)script->depth;
    newsp = js_AllocStack(cx, (uintN)(2 * depth), &mark);
    if (!newsp) {
	ok = JS_FALSE;
	sp = NULL;
	goto out;
    }
    newsp += depth;
    fp->sp = sp = newsp;

    while (pc < endpc) {
	fp->pc = pc;
	op = (JSOp)*pc;
      do_op:
	cs = &js_CodeSpec[op];
	len = cs->length;

#ifdef DEBUG
	tracefp = cx->tracefp;
	if (tracefp) {
	    intN nuses, n;

	    fprintf(tracefp, "%4u: ", js_PCToLineNumber(script, pc));
	    js_Disassemble1(cx, script, pc, pc - script->code, JS_FALSE,
			    tracefp);
	    nuses = cs->nuses;
	    if (nuses) {
		fp->sp = sp - nuses;
		for (n = 0; n < nuses; n++) {
		    str = js_DecompileValueGenerator(cx, *fp->sp, NULL);
		    if (str != NULL) {
			fprintf(tracefp, "%s %s",
				(n == 0) ? "  inputs:" : ",",
				JS_GetStringBytes(str));
		    }
		    fp->sp++;
		}
		putc('\n', tracefp);
	    }
	}
#endif

	if (rt->interruptHandler) {
	    JSTrapHandler handler = rt->interruptHandler;
	    /* check copy of pointer for safety in multithreaded situation */
	    if (handler) {
		switch (handler(cx, script, pc, &rval,
				rt->interruptHandlerData)) {
		  case JSTRAP_ERROR:
		    ok = JS_FALSE;
		    goto out;
		  case JSTRAP_CONTINUE:
		    break;
		  case JSTRAP_RETURN:
		    fp->rval = rval;
		    goto out;
#if JS_HAS_EXCEPTIONS
		  case JSTRAP_THROW:
                    cx->throwing = JS_TRUE;
                    cx->exception = rval;
                    ok = JS_FALSE;
		    goto out;
#endif /* JS_HAS_EXCEPTIONS */
		  default:;
		}
	    }
	}

	switch (op) {
	  case JSOP_NOP:
	    break;

	  case JSOP_PUSH:
	    PUSH_OPND(JSVAL_VOID);
	    break;

	  case JSOP_POP:
	    sp--;
	    break;

	  case JSOP_POP2:
	    sp -= 2;
	    break;

	  case JSOP_POPV:
	    *result = POP();
	    break;

	  case JSOP_ENTERWITH:
	    rval = POP();
	    VALUE_TO_OBJECT(cx, rval, obj);
	    withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain);
	    if (!withobj)
		goto out;
	    fp->scopeChain = withobj;
	    PUSH_OPND(OBJECT_TO_JSVAL(withobj));
	    break;

	  case JSOP_LEAVEWITH:
	    rval = POP();
	    JS_ASSERT(JSVAL_IS_OBJECT(rval));
	    withobj = JSVAL_TO_OBJECT(rval);
	    JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);

	    rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT);
	    JS_ASSERT(JSVAL_IS_OBJECT(rval));
	    fp->scopeChain = JSVAL_TO_OBJECT(rval);
	    break;

	  case JSOP_RETURN:
	    CHECK_BRANCH(-1);
	    fp->rval = POP();
	    goto out;

#if JS_HAS_SWITCH_STATEMENT
	  case JSOP_DEFAULT:
	    (void) POP();
	    /* fall through */
#endif
	  case JSOP_GOTO:
	    len = GET_JUMP_OFFSET(pc);
	    CHECK_BRANCH(len);
	    break;

	  case JSOP_IFEQ:
	    POP_BOOLEAN(cx, rval, cond);
	    if (cond == JS_FALSE) {
		len = GET_JUMP_OFFSET(pc);
		CHECK_BRANCH(len);
	    }
	    break;

	  case JSOP_IFNE:
	    POP_BOOLEAN(cx, rval, cond);
	    if (cond != JS_FALSE) {
		len = GET_JUMP_OFFSET(pc);
		CHECK_BRANCH(len);
	    }
	    break;

#if !JS_BUG_SHORT_CIRCUIT
	  case JSOP_OR:
	    POP_BOOLEAN(cx, rval, cond);
	    if (cond == JS_TRUE) {
		len = GET_JUMP_OFFSET(pc);
		PUSH_OPND(rval);
	    }
	    break;

	  case JSOP_AND:
	    POP_BOOLEAN(cx, rval, cond);
	    if (cond == JS_FALSE) {
		len = GET_JUMP_OFFSET(pc);
		PUSH_OPND(rval);
	    }
	    break;
#endif

	  case JSOP_TOOBJECT:
	    rval = POP();
	    SAVE_SP(fp);
	    ok = js_ValueToObject(cx, rval, &obj);
	    if (!ok)
		goto out;
	    PUSH(OBJECT_TO_JSVAL(obj));
	    break;

#define POP_ELEMENT_ID(id) {                                                  \
    /* If the index is not a jsint, atomize it. */                            \
    id = (jsid) POP();                                                        \
    if (JSVAL_IS_INT(id)) {                                                   \
	atom = NULL;                                                          \
    } else {                                                                  \
	atom = js_ValueToStringAtom(cx, (jsval)id);                           \
	if (!atom) {                                                          \
	    ok = JS_FALSE;                                                    \
	    goto out;                                                         \
	}                                                                     \
	id = (jsid)atom;                                                      \
    }                                                                         \
}

#if JS_HAS_IN_OPERATOR
	  case JSOP_IN:
	    rval = POP();
	    if (!JSVAL_IS_OBJECT(rval) || rval == JSVAL_NULL) {
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_IN_NOT_OBJECT);
		ok = JS_FALSE;
		goto out;
	    }
	    obj = JSVAL_TO_OBJECT(rval);
	    POP_ELEMENT_ID(id);
	    ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
	    if (!ok)
		goto out;
	    PUSH_OPND(BOOLEAN_TO_JSVAL(prop != NULL));
	    break;
#endif /* JS_HAS_IN_OPERATOR */

	  case JSOP_FORNAME:
	    rval = POP();
	    /* FALL THROUGH */
	  case JSOP_FORNAME2:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;

	    SAVE_SP(fp);
	    ok = js_FindVariable(cx, id, &obj, &obj2, &prop);
	    if (!ok)
		goto out;
	    JS_ASSERT(prop);
	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	    lval = OBJECT_TO_JSVAL(obj);
	    goto do_forinloop;

	  case JSOP_FORPROP:
	    rval = POP();
	    /* FALL THROUGH */
	  case JSOP_FORPROP2:
	    lval = POP();
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    goto do_forinloop;

	  case JSOP_FORELEM:
	    rval = POP();
	    /* FALL THROUGH */
	  case JSOP_FORELEM2:
	    POP_ELEMENT_ID(id);
	    lval = POP();

	  do_forinloop:
	    /*
	     * ECMA-compatible for/in evals the object just once, before loop.
	     */
	    if (cs->format & JOF_FOR2) {
		obj = JSVAL_TO_OBJECT(sp[-1]);
		vp = sp - 2;
	    } else {
		/* Old-style (pre-ECMA) JSOPs */
		SAVE_SP(fp);
		ok = js_ValueToObject(cx, rval, &obj);
		if (!ok)
		    goto out;
		vp = sp - 1;
	    }

	    /* If the thing to the right of 'in' has no properties, break. */
	    if (!obj) {
		rval = JSVAL_FALSE;
		goto end_forinloop;
	    }

	    /*
	     * Save the thing to the right of 'in' as origobj.  Later on we use
	     * this variable to suppress enumeration of shadowed prototype
	     * properties.
	     */
	    origobj = obj;

	    /*
	     * Reach under the top of stack to find our property iterator, a
	     * JSObject that contains the iteration state.  (An object is used
	     * rather than a native struct so that the iteration state is
	     * cleaned up via GC if the for-in loop terminates abruptly.)
	     */
	    rval = *vp;

	    /* Is this the first iteration ? */
	    if (JSVAL_IS_VOID(rval)) {
		/* Yes, create a new JSObject to hold the iterator state */
		propobj = js_NewObject(cx, &prop_iterator_class, NULL, NULL);
		if (!propobj) {
		    ok = JS_FALSE;
		    goto out;
		}

		/*
		 * Rewrite the iterator so we know to do the next case.
		 * Do this before calling the enumerator, which could
		 * displace cx->newborn and cause GC.
		 */
		*vp = OBJECT_TO_JSVAL(propobj);

		ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0);
		propobj->slots[JSSLOT_ITR_STATE] = iter_state;
		if (!ok)
		    goto out;

#if 1 //SRJMOD
		propobj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(OBJ_GET_CLASS(cx, obj)->flags);
#endif
		/*
		 * Stash private iteration state into property iterator object.
		 * NB: This code knows that the first slots are pre-allocated.
		 */
		JS_ASSERT(JS_INITIAL_NSLOTS >= 5);
		propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
	    } else {
		/* This is not the first iteration. Recover iterator state. */
		propobj = JSVAL_TO_OBJECT(rval);
		obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]);
		iter_state = propobj->slots[JSSLOT_ITR_STATE];
	    }

	  enum_next_property:
	    /* Get the next jsid to be enumerated and store it in rval */
	    OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &rval);
	    propobj->slots[JSSLOT_ITR_STATE] = iter_state;

	    /* No more jsids to iterate in obj ? */
	    if (iter_state == JSVAL_NULL) {
		/* Enumerate the properties on obj's prototype chain */
		obj = OBJ_GET_PROTO(cx, obj);
		if (!obj) {
		    /* End of property list -- terminate loop. */
		    rval = JSVAL_FALSE;
		    goto end_forinloop;
		}

		ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0);
		propobj->slots[JSSLOT_ITR_STATE] = iter_state;
		if (!ok)
		    goto out;

		/* Stash private iteration state into iterator JSObject. */
		propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
		goto enum_next_property;
	    }

	    /* Skip deleted and shadowed properties, leave next id in rval. */
	    if (obj != origobj) {
		/* Have we already enumerated a clone of this property? */
		ok = OBJ_LOOKUP_PROPERTY(cx, origobj, rval, &obj2, &prop);
		if (!ok)
		    goto out;
		if (prop) {
		    OBJ_DROP_PROPERTY(cx, obj2, prop);

		    /* Yes, don't enumerate again.  Go to the next property */
		    if (obj2 != obj)
			goto enum_next_property;
		}
	    }

	    /* Convert lval to a non-null object containing id. */
	    VALUE_TO_OBJECT(cx, lval, obj);

	    /* Make sure rval is a string for uniformity and compatibility. */
	    if (!JSVAL_IS_INT(rval)) {
		rval = ATOM_KEY((JSAtom *)rval);
	    } else if ((cx->version <= JSVERSION_1_1) &&
		       (cx->version >= JSVERSION_1_0)) {
		str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval));
		if (!str) {
		    ok = JS_FALSE;
		    goto out;
		}

		/* Hold via sp[0] in case the GC runs under OBJ_SET_PROPERTY. */
		rval = sp[0] = STRING_TO_JSVAL(str);
	    }

	    /* Set the variable obj[id] to refer to rval. */
	    ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
	    if (!ok)
		goto out;

	    /* Push true to keep looping through properties. */
	    rval = JSVAL_TRUE;

	  end_forinloop:
	    PUSH_OPND(rval);
	    break;

	  case JSOP_DUP:
	    JS_ASSERT(sp > newsp);
	    rval = sp[-1];
	    PUSH_OPND(rval);
	    break;

	  case JSOP_DUP2:
	    JS_ASSERT(sp - 1 > newsp);
	    lval = sp[-2];
	    rval = sp[-1];
	    PUSH_OPND(lval);
	    PUSH_OPND(rval);
	    break;

#define PROPERTY_OP(call) {                                                   \
    /* Pop the left part and resolve it to a non-null object. */              \
    lval = POP();                                                             \
    VALUE_TO_OBJECT(cx, lval, obj);                                           \
									      \
    /* Get or set the property, set ok false if error, true if success. */    \
    call;                                                                     \
    if (!ok)                                                                  \
	goto out;                                                             \
}

#define ELEMENT_OP(call) {                                                    \
    POP_ELEMENT_ID(id);                                                       \
    PROPERTY_OP(call);                                                        \
}

#define CACHED_GET(call) {                                                    \
    PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);                   \
    if (PROP_FOUND(prop)) {                                                   \
	sprop = (JSScopeProperty *)prop;                                      \
	slot = (uintN)sprop->slot;                                            \
	rval = OBJ_GET_SLOT(cx, obj, slot);                                   \
	ok = SPROP_GET(cx, sprop, obj, obj, &rval);                           \
	if (ok)                                                               \
	    OBJ_SET_SLOT(cx, obj, slot, rval);                                \
    } else {                                                                  \
	SAVE_SP(fp);                                                          \
	ok = call;                                                            \
	/* No fill here: js_GetProperty fills the cache. */                   \
    }                                                                         \
}

#if JS_BUG_SET_ENUMERATE
#define SET_ENUMERATE_ATTR(sprop) ((sprop)->attrs |= JSPROP_ENUMERATE)
#else
#define SET_ENUMERATE_ATTR(sprop) ((void)0)
#endif

#define CACHED_SET(call) {                                                    \
    PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);                   \
    if (PROP_FOUND(prop) &&                                                   \
	!(sprop = (JSScopeProperty *)prop,                                    \
	  sprop->attrs & (JSPROP_READONLY | JSPROP_ASSIGNHACK))) {            \
	ok = SPROP_SET(cx, sprop, obj, obj, &rval);                           \
	if (ok) {                                                             \
	    SET_ENUMERATE_ATTR(sprop);                                        \
            GC_POKE(cx, NULL);  /* second arg ignored! */                     \
	    OBJ_SET_SLOT(cx, obj, sprop->slot, rval);                         \
	}                                                                     \
    } else {                                                                  \
	SAVE_SP(fp);                                                          \
	ok = call;                                                            \
	/* No fill here: js_SetProperty writes through the cache. */          \
    }                                                                         \
}

	  case JSOP_SETNAME:
	    /* Get an immediate atom naming the variable to set. */
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;

	    SAVE_SP(fp);
	    ok = js_FindVariable(cx, id, &obj, &obj2, &prop);
	    if (!ok)
		goto out;
	    JS_ASSERT(prop);

	    OBJ_DROP_PROPERTY(cx, obj2, prop);

	    /* Pop the right-hand side and set obj[id] to it. */
	    rval = POP();

	    /* Try to hit the property cache, FindVariable primes it. */
	    CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
	    if (!ok)
		goto out;

	    /* Push the right-hand side as our result. */
	    PUSH_OPND(rval);
	    break;

	  case JSOP_BINDNAME:
	    atom = GET_ATOM(cx, script, pc);
	    SAVE_SP(fp);
	    ok = js_FindVariable(cx, (jsid)atom, &obj, &obj2, &prop);
	    if (!ok)
		goto out;
	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	    PUSH_OPND(OBJECT_TO_JSVAL(obj));
	    break;

	  case JSOP_SETNAME2:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    rval = POP();
	    lval = POP();
	    JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
	    obj  = JSVAL_TO_OBJECT(lval);
	    CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
	    if (!ok)
		goto out;
	    PUSH_OPND(rval);
	    break;

#define INTEGER_OP(OP, EXTRA_CODE) {                                          \
    SAVE_SP(fp);                                                              \
    ok = PopInt(cx, &j) && PopInt(cx, &i);                                    \
    RESTORE_SP(fp);                                                           \
    if (!ok)                                                                  \
	goto out;                                                             \
    EXTRA_CODE                                                                \
    i = i OP j;                                                               \
    PUSH_NUMBER(cx, i);                                                       \
}

#define BITWISE_OP(OP)          INTEGER_OP(OP, (void) 0;)
#define SIGNED_SHIFT_OP(OP)     INTEGER_OP(OP, j &= 31;)

	  case JSOP_BITOR:
	    BITWISE_OP(|);
	    break;

	  case JSOP_BITXOR:
	    BITWISE_OP(^);
	    break;

	  case JSOP_BITAND:
	    BITWISE_OP(&);
	    break;

#ifdef XP_PC
#define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN)                                \
    ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL))                         \
     ? (IFNAN)                                                                \
     : (LVAL) OP (RVAL))
#else
#define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL))
#endif

#define RELATIONAL_OP(OP) {                                                   \
    rval = POP();                                                             \
    lval = POP();                                                             \
    /* Optimize for two int-tagged operands (typical loop control). */        \
    if ((lval & rval) & JSVAL_INT) {                                          \
	ltmp = lval ^ JSVAL_VOID;                                             \
	rtmp = rval ^ JSVAL_VOID;                                             \
	if (ltmp && rtmp) {                                                   \
	    cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval);                  \
	} else {                                                              \
	    d  = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN;                      \
	    d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN;                      \
	    cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE);                      \
	}                                                                     \
    } else {                                                                  \
	VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval);                   \
	/* Need lval for strcmp or ToNumber later, so root it via sp[0]. */   \
	sp[0] = lval;                                                         \
	VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval);                   \
	if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) {                 \
	    str  = JSVAL_TO_STRING(lval);                                     \
	    str2 = JSVAL_TO_STRING(rval);                                     \
	    cond = js_CompareStrings(str, str2) OP 0;                         \
	} else {                                                              \
	    /* Need rval after ToNumber(lval), so root it via sp[1]. */       \
	    sp[1] = rval;                                                     \
	    VALUE_TO_NUMBER(cx, lval, d);                                     \
	    VALUE_TO_NUMBER(cx, rval, d2);                                    \
	    cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE);                      \
	}                                                                     \
    }                                                                         \
    PUSH_OPND(BOOLEAN_TO_JSVAL(cond));                                        \
}

#define EQUALITY_OP(OP, IFNAN) {                                              \
    rval = POP();                                                             \
    lval = POP();                                                             \
    ltmp = JSVAL_TAG(lval);                                                   \
    rtmp = JSVAL_TAG(rval);                                                   \
    if (ltmp == rtmp) {                                                       \
	if (ltmp == JSVAL_STRING) {                                           \
	    str  = JSVAL_TO_STRING(lval);                                     \
	    str2 = JSVAL_TO_STRING(rval);                                     \
	    cond = js_CompareStrings(str, str2) OP 0;                         \
	} else if (ltmp == JSVAL_DOUBLE) {                                    \
	    d  = *JSVAL_TO_DOUBLE(lval);                                      \
	    d2 = *JSVAL_TO_DOUBLE(rval);                                      \
	    cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                         \
	} else {                                                              \
	    /* Handle all undefined (=>NaN) and int combinations. */          \
	    cond = lval OP rval;                                              \
	}                                                                     \
    } else {                                                                  \
	if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) {                     \
	    cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1;         \
	} else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) {              \
	    cond = 1 OP 0;                                                    \
	} else {                                                              \
	    if (ltmp == JSVAL_OBJECT) {                                       \
		VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval);             \
		ltmp = JSVAL_TAG(lval);                                       \
	    } else if (rtmp == JSVAL_OBJECT) {                                \
		VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval);             \
		rtmp = JSVAL_TAG(rval);                                       \
	    }                                                                 \
	    if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) {               \
		str  = JSVAL_TO_STRING(lval);                                 \
		str2 = JSVAL_TO_STRING(rval);                                 \
		cond = js_CompareStrings(str, str2) OP 0;                     \
	    } else {                                                          \
		/* Need rval after ToNumber(lval), so root it via sp[1]. */   \
		sp[1] = rval;                                                 \
		VALUE_TO_NUMBER(cx, lval, d);                                 \
		VALUE_TO_NUMBER(cx, rval, d2);                                \
		cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \
	    }                                                                 \
	}                                                                     \
    }                                                                         \
    PUSH_OPND(BOOLEAN_TO_JSVAL(cond));                                        \
}

	  case JSOP_EQ:
	    EQUALITY_OP(==, JS_FALSE);
	    break;

	  case JSOP_NE:
	    EQUALITY_OP(!=, JS_TRUE);
	    break;

#if !JS_BUG_FALLIBLE_EQOPS
#define NEW_EQUALITY_OP(OP, IFNAN) {                                          \
    rval = POP();                                                             \
    lval = POP();                                                             \
    ltmp = JSVAL_TAG(lval);                                                   \
    rtmp = JSVAL_TAG(rval);                                                   \
    if (ltmp == rtmp) {                                                       \
	if (ltmp == JSVAL_STRING) {                                           \
	    str  = JSVAL_TO_STRING(lval);                                     \
	    str2 = JSVAL_TO_STRING(rval);                                     \
	    cond = js_CompareStrings(str, str2) OP 0;                         \
	} else if (ltmp == JSVAL_DOUBLE) {                                    \
	    d  = *JSVAL_TO_DOUBLE(lval);                                      \
	    d2 = *JSVAL_TO_DOUBLE(rval);                                      \
	    cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                         \
	} else {                                                              \
	    cond = lval OP rval;                                              \
	}                                                                     \
    } else {                                                                  \
	if (ltmp == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {                     \
	    d  = *JSVAL_TO_DOUBLE(lval);                                      \
	    d2 = JSVAL_TO_INT(rval);                                          \
	    cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                         \
	} else if (JSVAL_IS_INT(lval) && rtmp == JSVAL_DOUBLE) {              \
	    d  = JSVAL_TO_INT(lval);                                          \
	    d2 = *JSVAL_TO_DOUBLE(rval);                                      \
	    cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                         \
	} else {                                                              \
	    cond = lval OP rval;                                              \
	}                                                                     \
    }                                                                         \
    PUSH_OPND(BOOLEAN_TO_JSVAL(cond));                                        \
}

	  case JSOP_NEW_EQ:
	    NEW_EQUALITY_OP(==, JS_FALSE);
	    break;

	  case JSOP_NEW_NE:
	    NEW_EQUALITY_OP(!=, JS_TRUE);
	    break;

#if JS_HAS_SWITCH_STATEMENT
	  case JSOP_CASE:
	    NEW_EQUALITY_OP(==, JS_FALSE);
	    (void) POP();
	    if (cond) {
		len = GET_JUMP_OFFSET(pc);
		CHECK_BRANCH(len);
	    } else {
		PUSH(lval);
	    }
	    break;
#endif

#endif /* !JS_BUG_FALLIBLE_EQOPS */

	  case JSOP_LT:
	    RELATIONAL_OP(<);
	    break;

	  case JSOP_LE:
	    RELATIONAL_OP(<=);
	    break;

	  case JSOP_GT:
	    RELATIONAL_OP(>);
	    break;

	  case JSOP_GE:
	    RELATIONAL_OP(>=);
	    break;

#undef EQUALITY_OP
#undef RELATIONAL_OP

	  case JSOP_LSH:
	    SIGNED_SHIFT_OP(<<);
	    break;

	  case JSOP_RSH:
	    SIGNED_SHIFT_OP(>>);
	    break;

	  case JSOP_URSH:
	  {
	    uint32 u;

	    SAVE_SP(fp);
	    ok = PopInt(cx, &j) && PopUint(cx, &u);
	    RESTORE_SP(fp);
	    if (!ok)
		goto out;
	    j &= 31;
	    d = u >> j;
	    PUSH_NUMBER(cx, d);
	    break;
	  }

#undef INTEGER_OP
#undef BITWISE_OP
#undef SIGNED_SHIFT_OP

	  case JSOP_ADD:
	    rval = rtmp = POP();
	    lval = ltmp = POP();
	    VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval);
	    if ((cond = JSVAL_IS_STRING(lval)) != 0) {
		/*
		 * Keep lval on the stack so it isn't GC'd during either the
		 * next VALUE_TO_PRIMITIVE or the js_ValueToString(cx, rval).
		 */
		sp[0] = lval;
	    }
	    VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval);
	    if (cond || JSVAL_IS_STRING(rval)) {
		if (cond) {
		    str = JSVAL_TO_STRING(lval);
		    SAVE_SP(fp);
		    ok = (str2 = js_ValueToString(cx, rval)) != NULL;
		} else {
		    /*
		     * Keep rval on the stack so it isn't GC'd during the next
		     * js_ValueToString.
		     */
		    sp[1] = rval;
		    str2 = JSVAL_TO_STRING(rval);
		    SAVE_SP(fp);
		    ok = (str = js_ValueToString(cx, lval)) != NULL;
		}
		if (!ok)
		    goto out;
		if ((length = str->length) == 0) {
		    str3 = str2;
		} else if ((length2 = str2->length) == 0) {
		    str3 = str;
		} else {
		    length3 = length + length2;
		    chars = JS_malloc(cx, (length3 + 1) * sizeof(jschar));
		    if (!chars) {
			ok = JS_FALSE;
			goto out;
		    }
		    js_strncpy(chars, str->chars, length);
		    js_strncpy(chars + length, str2->chars, length2);
		    chars[length3] = 0;
		    str3 = js_NewString(cx, chars, length3, 0);
		    if (!str3) {
			JS_free(cx, chars);
			ok = JS_FALSE;
			goto out;
		    }
		}
		PUSH_OPND(STRING_TO_JSVAL(str3));
	    } else {
		VALUE_TO_NUMBER(cx, ltmp, d);
		VALUE_TO_NUMBER(cx, rtmp, d2);
		d += d2;
		PUSH_NUMBER(cx, d);
	    }
	    break;

#define BINARY_OP(OP) {                                                       \
    POP_NUMBER(cx, d2);                                                       \
    POP_NUMBER(cx, d);                                                        \
    d = d OP d2;                                                              \
    PUSH_NUMBER(cx, d);                                                       \
}

	  case JSOP_SUB:
	    BINARY_OP(-);
	    break;

	  case JSOP_MUL:
	    BINARY_OP(*);
	    break;

	  case JSOP_DIV:
	    POP_NUMBER(cx, d2);
	    POP_NUMBER(cx, d);
	    if (d2 == 0) {
#ifdef XP_PC
		/* XXX MSVC miscompiles such that (NaN == 0) */
		if (JSDOUBLE_IS_NaN(d2))
		    rval = DOUBLE_TO_JSVAL(rt->jsNaN);
		else
#endif
		if (d == 0 || JSDOUBLE_IS_NaN(d))
		    rval = DOUBLE_TO_JSVAL(rt->jsNaN);
		else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
		    rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
		else
		    rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
		PUSH_OPND(rval);
	    } else {
		d /= d2;
		PUSH_NUMBER(cx, d);
	    }
	    break;

	  case JSOP_MOD:
	    POP_NUMBER(cx, d2);
	    POP_NUMBER(cx, d);
	    if (d2 == 0) {
		PUSH_OPND(DOUBLE_TO_JSVAL(rt->jsNaN));
	    } else {
#ifdef XP_PC
	      /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
	      if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
#endif
		d = fmod(d, d2);
		PUSH_NUMBER(cx, d);
	    }
	    break;

	  case JSOP_NOT:
	    POP_BOOLEAN(cx, rval, cond);
	    PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
	    break;

	  case JSOP_BITNOT:
	    SAVE_SP(fp);
	    ok = PopInt(cx, &i);
	    RESTORE_SP(fp);
	    if (!ok)
		goto out;
	    i = ~i;
	    PUSH_NUMBER(cx, i);
	    break;

	  case JSOP_NEG:
	    POP_NUMBER(cx, d);
#ifdef HPUX
            /* 
             * Negation of a zero doesn't produce a negative
             * zero on HPUX. Perform the operation by bit
             * twiddling.
             */
            JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
#else
	    d = -d;
#endif
	    PUSH_NUMBER(cx, d);
	    break;

	  case JSOP_POS:
	    POP_NUMBER(cx, d);
	    PUSH_NUMBER(cx, d);
	    break;

	  case JSOP_NEW:
	    /* Get immediate argc and find the constructor function. */
	    argc = GET_ARGC(pc);

	  do_new:
	    vp = sp - (2 + argc);
	    JS_ASSERT(vp >= newsp);

	    fun = NULL;
	    obj2 = NULL;
	    lval = *vp;
	    if (!JSVAL_IS_OBJECT(lval) ||
		(obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
		/* XXX clean up to avoid special cases above ObjectOps layer */
		(clasp = OBJ_GET_CLASS(cx, obj2),
		 clasp == &js_FunctionClass || clasp == &js_ClosureClass) ||
                 !obj2->map->ops->construct) 
            {
		fun = js_ValueToFunction(cx, vp, JS_TRUE);
		if (!fun) {
		    ok = JS_FALSE;
		    goto out;
		}
		obj2 = fun->object;
	    }

            clasp = &js_ObjectClass;
	    if (!obj2) {
		proto = parent = NULL;
		fun = NULL;
	    } else {
                JSClass *cl;

		/* Get the constructor prototype object for this function. */
		ok = OBJ_GET_PROPERTY(cx, obj2,
				      (jsid)rt->atomState.classPrototypeAtom,
				      &rval);
		if (!ok)
		    goto out;
		proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
		parent = OBJ_GET_PARENT(cx, obj2);

                if ((OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) && 
                    (cl = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp) != 0)
                {
                    clasp = cl;
                }
	    }
	    obj = js_NewObject(cx, clasp, proto, parent);
	    if (!obj) {
		ok = JS_FALSE;
		goto out;
	    }

	    /* Now we have an object with a constructor method; call it. */
	    vp[1] = OBJECT_TO_JSVAL(obj);
	    SAVE_SP(fp);
	    ok = js_Invoke(cx, argc, JS_TRUE);
	    RESTORE_SP(fp);
	    if (!ok) {
		cx->newborn[GCX_OBJECT] = NULL;
		goto out;
	    }

	    /* Check the return value and update obj from it. */
	    rval = *vp;
	    if (JSVAL_IS_PRIMITIVE(rval)) {
		if (fun || !JSVERSION_IS_ECMA(cx->version)) {
		    *vp = OBJECT_TO_JSVAL(obj);
		    break;
		}
		/* native [[Construct]] returning primitive is error */
		str = js_ValueToString(cx, rval);
		if (str) {
		    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
					 JSMSG_BAD_NEW_RESULT,
					 JS_GetStringBytes(str));
		}
		ok = JS_FALSE;
		goto out;
	    }
	    obj = JSVAL_TO_OBJECT(rval);
	    break;

	  case JSOP_DELNAME:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;

	    ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
	    if (!ok)
		goto out;

	    /* ECMA says to return true if name is undefined or inherited. */
	    rval = JSVAL_TRUE;
	    if (prop) {
		OBJ_DROP_PROPERTY(cx, obj2, prop);
		ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval);
		if (!ok)
		    goto out;
	    }
	    PUSH_OPND(rval);
	    break;

	  case JSOP_DELPROP:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    SAVE_SP(fp);
	    PROPERTY_OP(ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_DELELEM:
	    SAVE_SP(fp);
	    ELEMENT_OP(ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_TYPEOF:
	    rval = POP();
	    type = JS_TypeOfValue(cx, rval);
	    atom = rt->atomState.typeAtoms[type];
	    str  = ATOM_TO_STRING(atom);
	    PUSH_OPND(STRING_TO_JSVAL(str));
	    break;

	  case JSOP_VOID:
	    (void) POP();
	    PUSH_OPND(JSVAL_VOID);
	    break;

	  case JSOP_INCNAME:
	  case JSOP_DECNAME:
	  case JSOP_NAMEINC:
	  case JSOP_NAMEDEC:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;

	    SAVE_SP(fp);
	    ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
	    if (!ok)
		goto out;
	    if (!prop) {
		js_ReportIsNotDefined(cx, ATOM_BYTES(atom));
		ok = JS_FALSE;
		goto out;
	    }

	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	    lval = OBJECT_TO_JSVAL(obj);
	    goto do_incop;

	  case JSOP_INCPROP:
	  case JSOP_DECPROP:
	  case JSOP_PROPINC:
	  case JSOP_PROPDEC:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    lval = POP();
	    goto do_incop;

	  case JSOP_INCELEM:
	  case JSOP_DECELEM:
	  case JSOP_ELEMINC:
	  case JSOP_ELEMDEC:
	    POP_ELEMENT_ID(id);
	    lval = POP();

	  do_incop:
	    VALUE_TO_OBJECT(cx, lval, obj);

	    /* The operand must contain a number. */
	    CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
	    if (!ok)
		goto out;
	    VALUE_TO_NUMBER(cx, rval, d);

	    /* Compute the post- or pre-incremented value. */
	    if (cs->format & JOF_INC)
		d2 = (cs->format & JOF_POST) ? d++ : ++d;
	    else
		d2 = (cs->format & JOF_POST) ? d-- : --d;

	    /* Set obj[id] to the resulting number. */
	    ok = js_NewNumberValue(cx, d, &rval);
	    if (!ok)
		goto out;
	    CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
	    if (!ok)
		goto out;
	    PUSH_NUMBER(cx, d2);
	    break;

	  case JSOP_INCARG:
	  case JSOP_DECARG:
	  case JSOP_ARGINC:
	  case JSOP_ARGDEC:
	    slot = (uintN)GET_ARGNO(pc);
	    JS_ASSERT(slot < fp->fun->nargs);
	    rval = fp->argv[slot];
	    VALUE_TO_NUMBER(cx, rval, d);

	    /* Compute the post- or pre-incremented value. */
	    if (cs->format & JOF_INC)
		d2 = (cs->format & JOF_POST) ? d++ : ++d;
	    else
		d2 = (cs->format & JOF_POST) ? d-- : --d;

	    ok = js_NewNumberValue(cx, d, &rval);
	    if (!ok)
		goto out;
	    fp->argv[slot] = rval;
	    PUSH_NUMBER(cx, d2);
	    break;

	  case JSOP_INCVAR:
	  case JSOP_DECVAR:
	  case JSOP_VARINC:
	  case JSOP_VARDEC:
	    slot = (uintN)GET_VARNO(pc);
	    JS_ASSERT(slot < fp->fun->nvars);
	    rval = fp->vars[slot];
	    VALUE_TO_NUMBER(cx, rval, d);

	    /* Compute the post- or pre-incremented value. */
	    if (cs->format & JOF_INC)
		d2 = (cs->format & JOF_POST) ? d++ : ++d;
	    else
		d2 = (cs->format & JOF_POST) ? d-- : --d;

	    ok = js_NewNumberValue(cx, d, &rval);
	    if (!ok)
		goto out;
	    fp->vars[slot] = rval;
	    PUSH_NUMBER(cx, d2);
	    break;

	  case JSOP_GETPROP:
	    /* Get an immediate atom naming the property. */
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    PROPERTY_OP(CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_SETPROP:
	    /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */
	    rval = POP();

	    /* Get an immediate atom naming the property. */
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    PROPERTY_OP(CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_GETELEM:
	    ELEMENT_OP(CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_SETELEM:
	    rval = POP();
	    ELEMENT_OP(CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_PUSHOBJ:
	    PUSH_OPND(OBJECT_TO_JSVAL(obj));
	    break;

	  case JSOP_CALLSPECIAL:
	  case JSOP_CALL:
	    argc = GET_ARGC(pc);
	    SAVE_SP(fp);
	    ok = js_Invoke(cx, argc, JS_FALSE);
	    RESTORE_SP(fp);
	    if (!ok) {
		goto out;
	    }
	    obj = NULL;
	    break;

	  case JSOP_NAME:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;

	    ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
	    if (!ok)
		goto out;
	    if (!prop) {
		/* Kludge to allow (typeof foo == "undefined") tests. */
		for (pc2 = pc + len; pc2 < endpc; pc2++) {
		    op2 = (JSOp)*pc2;
		    if (op2 == JSOP_TYPEOF) {
			PUSH_OPND(JSVAL_VOID);
			goto advance_pc;
		    }
		    if (op2 != JSOP_NOP)
			break;
		}
		js_ReportIsNotDefined(cx, ATOM_BYTES(atom));
		ok = JS_FALSE;
		goto out;
	    }

	    /* Take the slow path if prop was not found in a native object. */
	    if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
		OBJ_DROP_PROPERTY(cx, obj2, prop);
		ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
		if (!ok)
		    goto out;
		PUSH_OPND(rval);
		break;
	    }

	    /* Get and push the obj[id] property's value. */
	    sprop = (JSScopeProperty *)prop;
	    slot = (uintN)sprop->slot;
	    rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
	    JS_UNLOCK_OBJ(cx, obj2);
	    ok = SPROP_GET(cx, sprop, obj, obj2, &rval);
	    JS_LOCK_OBJ(cx, obj2);
	    if (!ok) {
		OBJ_DROP_PROPERTY(cx, obj2, prop);
		goto out;
	    }
	    LOCKED_OBJ_SET_SLOT(obj2, slot, rval);
	    OBJ_DROP_PROPERTY(cx, obj2, prop);
	    PUSH_OPND(rval);
	    break;

	  case JSOP_UINT16:
	    i = (jsint) GET_ATOM_INDEX(pc);
	    rval = INT_TO_JSVAL(i);
	    PUSH_OPND(rval);
	    break;

	  case JSOP_NUMBER:
	  case JSOP_STRING:
	    atom = GET_ATOM(cx, script, pc);
	    PUSH_OPND(ATOM_KEY(atom));
	    break;

	  case JSOP_OBJECT:
	    atom = GET_ATOM(cx, script, pc);
	    rval = ATOM_KEY(atom);
	    JS_ASSERT(JSVAL_IS_OBJECT(rval));
	    PUSH_OPND(rval);
	    break;

	  case JSOP_ZERO:
	    PUSH_OPND(JSVAL_ZERO);
	    break;

	  case JSOP_ONE:
	    PUSH_OPND(JSVAL_ONE);
	    break;

	  case JSOP_NULL:
	    PUSH_OPND(JSVAL_NULL);
	    break;

	  case JSOP_THIS:
	    PUSH_OPND(OBJECT_TO_JSVAL(fp->thisp));
	    break;

	  case JSOP_FALSE:
	    PUSH_OPND(JSVAL_FALSE);
	    break;

	  case JSOP_TRUE:
	    PUSH_OPND(JSVAL_TRUE);
	    break;

#if JS_HAS_SWITCH_STATEMENT
	  case JSOP_TABLESWITCH:
	    pc2 = pc;
	    len = GET_JUMP_OFFSET(pc2);

	    /*
	     * ECMAv2 forbids conversion of discriminant, so we will skip to
	     * the default case if the discriminant isn't an int jsval.
	     * (This opcode is emitted only for dense jsint-domain switches.)
	     */
	    if (cx->version == JSVERSION_DEFAULT ||
		cx->version >= JSVERSION_1_4) {
		rval = POP();
		if (!JSVAL_IS_INT(rval))
		    break;
		i = JSVAL_TO_INT(rval);
	    } else {
		SAVE_SP(fp);
		ok = PopInt(cx, &i);
		RESTORE_SP(fp);
		if (!ok)
		    goto out;
	    }

	    pc2 += 2;
	    low = GET_JUMP_OFFSET(pc2);
	    pc2 += 2;
	    high = GET_JUMP_OFFSET(pc2);

	    i -= low;
	    if ((jsuint)i <= (jsuint)(high - low)) {
		pc2 += 2 + 2 * i;
		off = GET_JUMP_OFFSET(pc2);
		if (off)
		    len = off;
	    }
	    break;

	  case JSOP_LOOKUPSWITCH:
	    lval = POP();
	    pc2 = pc;
	    len = GET_JUMP_OFFSET(pc2);

	    if (!JSVAL_IS_NUMBER(lval) &&
		!JSVAL_IS_STRING(lval) &&
		!JSVAL_IS_BOOLEAN(lval)) {
		goto advance_pc;
	    }

	    pc2 += 2;
	    npairs = GET_ATOM_INDEX(pc2);
	    pc2 += 2;

#define SEARCH_PAIRS(MATCH_CODE)                                              \
    while (npairs) {                                                          \
	atom = GET_ATOM(cx, script, pc2);                                     \
	rval = ATOM_KEY(atom);                                                \
	MATCH_CODE                                                            \
	if (match) {                                                          \
	    pc2 += 2;                                                         \
	    len = GET_JUMP_OFFSET(pc2);                                       \
	    goto advance_pc;                                                  \
	}                                                                     \
	pc2 += 4;                                                             \
	npairs--;                                                             \
    }
	    if (JSVAL_IS_STRING(lval)) {
		str  = JSVAL_TO_STRING(lval);
		SEARCH_PAIRS(
		    match = (JSVAL_IS_STRING(rval) &&
			     ((str2 = JSVAL_TO_STRING(rval)) == str ||
			      !js_CompareStrings(str2, str)));
		)
	    } else if (JSVAL_IS_DOUBLE(lval)) {
		d = *JSVAL_TO_DOUBLE(lval);
		SEARCH_PAIRS(
		    match = (JSVAL_IS_DOUBLE(rval) &&
			     *JSVAL_TO_DOUBLE(rval) == d);
		)
	    } else {
		SEARCH_PAIRS(
		    match = (lval == rval);
		)
	    }
#undef SEARCH_PAIRS
	    break;

	  case JSOP_CONDSWITCH:
	    break;

#endif /* JS_HAS_SWITCH_STATEMENT */

#if JS_HAS_LEXICAL_CLOSURE
	  case JSOP_CLOSURE:
	    /*
	     * If the nearest variable scope is a function, not a call object,
	     * replace it in the scope chain with its call object.
	     */
	    obj = js_FindVariableScope(cx, &fun);
	    if (!obj) {
		ok = JS_FALSE;
		goto out;
	    }

	    /*
	     * If in a with statement, set obj to the With object's prototype,
	     * i.e., the object specified in the with statement head.
	     */
	    if (fp->scopeChain != obj) {
		obj = fp->scopeChain;
		JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_WithClass);
#if JS_BUG_WITH_CLOSURE
		while (OBJ_GET_CLASS(cx, obj) == &js_WithClass) {
		    proto = OBJ_GET_PROTO(cx, obj);
		    if (!proto)
			break;
		    obj = proto;
		}
#endif
	    }

	    /*
	     * Get immediate operand atom, which is a function object literal.
	     * From it, get the function to close.
	     */
	    atom = GET_ATOM(cx, script, pc);
	    JS_ASSERT(ATOM_IS_OBJECT(atom));
	    obj2 = ATOM_TO_OBJECT(atom);
	    fun2 = JS_GetPrivate(cx, obj2);

	    /*
	     * Let closure = new Closure(obj2).
	     * NB: js_ConstructObject does not use the "constructor" property
	     * of the new object it creates, because in this case and others
	     * such as js_WithClass, that property refers to the prototype's
	     * constructor function.
	     */
	    closure = js_ConstructObject(cx, &js_ClosureClass, obj2,
					 fp->scopeChain);
	    if (!closure) {
		ok = JS_FALSE;
		goto out;
	    }

	    /*
	     * Define a property in obj with id fun2->atom and value closure,
	     * but only if fun2 is not anonymous.
	     */
	    if (fun2->atom) {
		ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)fun2->atom,
					 OBJECT_TO_JSVAL(closure),
					 NULL, NULL, JSPROP_ENUMERATE,
					 NULL);
		if (!ok) {
		    cx->newborn[GCX_OBJECT] = NULL;
		    goto out;
		}
	    }
	    PUSH_OPND(OBJECT_TO_JSVAL(closure));
	    break;
#endif /* JS_HAS_LEXICAL_CLOSURE */

#if JS_HAS_EXPORT_IMPORT
	  case JSOP_EXPORTALL:
	    obj = js_FindVariableScope(cx, &fun);
	    if (!obj) {
		ok = JS_FALSE;
	    } else {
		ida = JS_Enumerate(cx, obj);
		if (!ida) {
		    ok = JS_FALSE;
		} else {
		    for (i = 0, j = ida->length; i < j; i++) {
			id = ida->vector[i];
			ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
			if (!ok)
			    break;
			if (!prop)
			    continue;
			ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
			if (ok) {
			    attrs |= JSPROP_EXPORTED;
			    ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
			}
			OBJ_DROP_PROPERTY(cx, obj2, prop);
			if (!ok)
			    break;
		    }
		    JS_DestroyIdArray(cx, ida);
		}
	    }
	    break;

	  case JSOP_EXPORTNAME:
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    obj  = js_FindVariableScope(cx, &fun);
	    if (!obj) {
		ok = JS_FALSE;
		goto out;
	    }
	    ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
	    if (!ok)
		goto out;
	    if (!prop) {
		ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
					 JSPROP_EXPORTED, NULL);
	    } else {
		ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
		if (ok) {
		    attrs |= JSPROP_EXPORTED;
		    ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
		}
		OBJ_DROP_PROPERTY(cx, obj2, prop);
	    }
	    if (!ok)
		goto out;
	    break;

	  case JSOP_IMPORTALL:
	    id = (jsid)JSVAL_VOID;
	    PROPERTY_OP(ok = ImportProperty(cx, obj, id));
	    break;

	  case JSOP_IMPORTPROP:
	    /* Get an immediate atom naming the property. */
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    PROPERTY_OP(ok = ImportProperty(cx, obj, id));
	    break;

	  case JSOP_IMPORTELEM:
	    ELEMENT_OP(ok = ImportProperty(cx, obj, id));
	    break;
#endif /* JS_HAS_EXPORT_IMPORT */

	  case JSOP_TRAP:
	    switch (JS_HandleTrap(cx, script, pc, &rval)) {
	      case JSTRAP_ERROR:
		ok = JS_FALSE;
		goto out;
	      case JSTRAP_CONTINUE:
		JS_ASSERT(JSVAL_IS_INT(rval));
		op = (JSOp) JSVAL_TO_INT(rval);
		JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
		goto do_op;
	      case JSTRAP_RETURN:
		fp->rval = rval;
		goto out;
#if JS_HAS_EXCEPTIONS
	      case JSTRAP_THROW:
                cx->throwing = JS_TRUE;
                cx->exception = rval;
                ok = JS_FALSE;
	        goto out;
#endif /* JS_HAS_EXCEPTIONS */
	      default:;
	    }
	    break;

	  case JSOP_GETARG:
	    obj = NULL;
	    slot = (uintN)GET_ARGNO(pc);
	    JS_ASSERT(slot < fp->fun->nargs);
	    PUSH_OPND(fp->argv[slot]);
	    break;

	  case JSOP_SETARG:
	    obj = NULL;
	    slot = (uintN)GET_ARGNO(pc);
	    JS_ASSERT(slot < fp->fun->nargs);
	    vp = &fp->argv[slot];
	    GC_POKE(cx, *vp);
	    *vp = sp[-1];
	    break;

	  case JSOP_GETVAR:
	    obj = NULL;
	    slot = (uintN)GET_VARNO(pc);
	    JS_ASSERT(slot < fp->fun->nvars);
	    PUSH_OPND(fp->vars[slot]);
	    break;

	  case JSOP_SETVAR:
	    obj = NULL;
	    slot = (uintN)GET_VARNO(pc);
	    JS_ASSERT(slot < fp->fun->nvars);
	    vp = &fp->vars[slot];
	    GC_POKE(cx, *vp);
	    *vp = sp[-1];
	    break;

#if JS_HAS_INITIALIZERS
	  case JSOP_NEWINIT:
	    argc = 0;
	    fp->sharpDepth++;
	    goto do_new;

	  case JSOP_ENDINIT:
	    if (--fp->sharpDepth == 0)
		fp->sharpArray = NULL;
	    break;

	  case JSOP_INITPROP:
	    /* Pop the property's value into rval. */
	    JS_ASSERT(sp - newsp >= 2);
	    rval = POP();

	    /* Get the immediate property name into id. */
	    atom = GET_ATOM(cx, script, pc);
	    id   = (jsid)atom;
	    goto do_init;

	  case JSOP_INITELEM:
	    /* Pop the element's value into rval. */
	    JS_ASSERT(sp - newsp >= 3);
	    rval = POP();

	    /* Pop and conditionally atomize the element id. */
	    POP_ELEMENT_ID(id);

	  do_init:
	    /* Find the object being initialized at top of stack. */
	    lval = sp[-1];
	    JS_ASSERT(JSVAL_IS_OBJECT(lval));
	    obj = JSVAL_TO_OBJECT(lval);

	    /* Set the property named by obj[id] to rval. */
	    ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
	    if (!ok)
		goto out;
	    break;

#if JS_HAS_SHARP_VARS
	  case JSOP_DEFSHARP:
	    obj = fp->sharpArray;
	    if (!obj) {
		obj = js_NewArrayObject(cx, 0, NULL);
		if (!obj) {
		    ok = JS_FALSE;
		    goto out;
		}
		fp->sharpArray = obj;
	    }
	    i = (jsint) GET_ATOM_INDEX(pc);
	    id = (jsid) INT_TO_JSVAL(i);
	    rval = sp[-1];
	    if (JSVAL_IS_PRIMITIVE(rval)) {
		char numBuf[12];
		JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_BAD_SHARP_DEF, numBuf);
		ok = JS_FALSE;
		goto out;
	    }
	    ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
	    if (!ok)
		goto out;
	    break;

	  case JSOP_USESHARP:
	    i = (jsint) GET_ATOM_INDEX(pc);
	    id = (jsid) INT_TO_JSVAL(i);
	    obj = fp->sharpArray;
	    if (!obj) {
		rval = JSVAL_VOID;
	    } else {
		ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
		if (!ok)
		    goto out;
	    }
	    if (!JSVAL_IS_OBJECT(rval)) {
		char numBuf[12];
		JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_BAD_SHARP_USE, numBuf);
		ok = JS_FALSE;
		goto out;
	    }
	    PUSH_OPND(rval);
	    break;
#endif /* JS_HAS_SHARP_VARS */
#endif /* JS_HAS_INITIALIZERS */

#if JS_HAS_EXCEPTIONS
	  /* reset the stack to the given depth */
	  case JSOP_SETSP:
	    i = (jsint) GET_ATOM_INDEX(pc);
	    JS_ASSERT(i >= 0);
	    sp = newsp + i;
	    break;

	  case JSOP_GOSUB:
	    len = GET_JUMP_OFFSET(pc);
	    JS_ASSERT(js_CodeSpec[JSOP_GOSUB].length == 3);
	    i = pc - script->code + 3;
	    PUSH(INT_TO_JSVAL(i));
	    break;

	  case JSOP_RETSUB:
	    rval = POP();
	    JS_ASSERT(JSVAL_IS_INT(rval));
	    i = JSVAL_TO_INT(rval);
	    pc = script->code + i;
	    len = 0;
	    break;

	  case JSOP_EXCEPTION:
	    PUSH(cx->exception);
	    break;

	  case JSOP_THROW:
	    cx->throwing = JS_TRUE;
	    cx->exception = POP();
	    ok = JS_FALSE;
            /* let the code at out try to catch the exception. */
	    goto out;
#endif /* JS_HAS_EXCEPTIONS */

#if JS_HAS_INSTANCEOF
	  case JSOP_INSTANCEOF:
	    rval = POP();
	    if (JSVAL_IS_PRIMITIVE(rval)) {
		str = js_DecompileValueGenerator(cx, rval, NULL);
		if (str) {
		    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
					 JSMSG_BAD_INSTANCEOF_RHS,
					 JS_GetStringBytes(str));
		}
		ok = JS_FALSE;
		goto out;
	    }
	    obj = JSVAL_TO_OBJECT(rval);
	    lval = POP();
	    cond = JS_FALSE;
	    if (obj->map->ops->hasInstance) {
		SAVE_SP(fp);
		ok = obj->map->ops->hasInstance(cx, obj, lval, &cond);
		if (!ok)
		    goto out;
	    }
	    PUSH_OPND(BOOLEAN_TO_JSVAL(cond));
	    break;
#endif /* JS_HAS_INSTANCEOF */

#if JS_HAS_DEBUGGER_KEYWORD
	  case JSOP_DEBUGGER:
	    if (rt->debuggerHandler) {
		JSTrapHandler handler = rt->debuggerHandler;
		/* check copy of pointer for safety in multithread situation */
		if (handler) {
		    switch (handler(cx, script, pc, &rval,
				    rt->debuggerHandlerData)) {
		      case JSTRAP_ERROR:
			ok = JS_FALSE;
			goto out;
		      case JSTRAP_CONTINUE:
			break;
		      case JSTRAP_RETURN:
			fp->rval = rval;
			goto out;
#if JS_HAS_EXCEPTIONS
		      case JSTRAP_THROW:
                        cx->throwing = JS_TRUE;
                        cx->exception = rval;
                        ok = JS_FALSE;
		        goto out;
#endif /* JS_HAS_EXCEPTIONS */
		      default:;
		    }
		}
	    }
	    break;
#endif /* JS_HAS_DEBUGGER_KEYWORD */

	  default: {
	    char numBuf[12];
	    JS_snprintf(numBuf, sizeof numBuf, "%d", op);
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_BAD_BYTECODE, numBuf);
	    ok = JS_FALSE;
	    goto out;
	  }
	}

    advance_pc:
	pc += len;

#ifdef DEBUG
	if (tracefp) {
	    intN ndefs, n;

	    ndefs = cs->ndefs;
	    if (ndefs) {
		fp->sp = sp - ndefs;
		for (n = 0; n < ndefs; n++) {
		    str = js_DecompileValueGenerator(cx, *fp->sp, NULL);
		    if (str) {
			fprintf(tracefp, "%s %s",
				(n == 0) ? "  output:" : ",",
				JS_GetStringBytes(str));
		    }
		    fp->sp++;
		}
		putc('\n', tracefp);
	    }
	}
#endif
    }
out:

#if JS_HAS_EXCEPTIONS
    /* 
     * Has an exception been raised?
     */
    if (!ok && cx->throwing) {
        /* 
         * call hook if set
         */
	if (rt->throwHook) {
	    JSTrapHandler handler = rt->throwHook;
	    /* check copy of pointer for safety in multithreaded situation */
	    if (handler) {
		switch (handler(cx, script, pc, &rval,
				rt->throwHookData)) {
		  case JSTRAP_ERROR:
                    cx->throwing = JS_FALSE;
		    goto no_catch;
		  case JSTRAP_CONTINUE:
                    cx->throwing = JS_FALSE;
                    ok = JS_TRUE;
                    goto advance_pc;
		  case JSTRAP_RETURN:
                    ok = JS_TRUE;
                    cx->throwing = JS_FALSE;
		    fp->rval = rval;
		    goto no_catch;
		  case JSTRAP_THROW:
                    cx->exception = rval;
		  default:;
		}
	    }
	}

        /*
         * Look for a try block within this frame that can catch the exception.
         */ 
        tn = script->trynotes;
        if (tn) {
            offset = PTRDIFF(pc, script->code, jsbytecode);
            while (JS_UPTRDIFF(offset, tn->start) >= (jsuword)tn->length)
                tn++;
            if (tn->catchStart) {
                pc = script->code + tn->catchStart;
                len = 0;
                cx->throwing = JS_FALSE; /* caught */
                ok = JS_TRUE;
                goto advance_pc;
            }
        }
    }
no_catch:
#endif

    /*
     * Restore the previous frame's execution state.
     */
    js_FreeStack(cx, mark);
    cx->interpLevel--;
    return ok;
}

**** End of jsinterp.c. ****

**** Start of jsinterp.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsinterp_h___
#define jsinterp_h___
/*
 * JS interpreter interface.
 */
#include "jsprvtd.h"
#include "jspubtd.h"

/*
 * JS stack frame, allocated on the C stack.
 */
struct JSStackFrame {
    JSObject        *callobj;       /* lazily created Call object */
    JSObject        *argsobj;       /* lazily created arguments object */
    JSScript        *script;        /* script being interpreted */
    JSFunction      *fun;           /* function being called or null */
    JSObject        *thisp;         /* "this" pointer if in method */
    uintN           argc;           /* actual argument count */
    jsval           *argv;          /* base of argument stack slots */
    jsval           rval;           /* function return value */
    uintN           nvars;          /* local variable count */
    jsval           *vars;          /* base of variable stack slots */
    JSStackFrame    *down;          /* previous frame */
    void            *annotation;    /* used by Java security */
    JSObject        *scopeChain;    /* scope chain */
    jsbytecode      *pc;            /* program counter */
    jsval           *sp;            /* stack pointer */
    uintN           sharpDepth;     /* array/object initializer depth */
    JSObject        *sharpArray;    /* scope for #n= initializer vars */
    JSPackedBool    constructing;   /* true if called via new operator */
    uint8           overrides;      /* bit-set of overridden Call properties */
    JSPackedBool    debugging;      /* true if for JS_EvaluateInStackFrame */
    JSStackFrame    *dormantNext;   /* next dormant frame chain */
};

/*
 * Property cache for quickened get/set property opcodes.
 */
#define PROPERTY_CACHE_LOG2     10
#define PROPERTY_CACHE_SIZE     JS_BIT(PROPERTY_CACHE_LOG2)
#define PROPERTY_CACHE_MASK     JS_BITMASK(PROPERTY_CACHE_LOG2)

#define PROPERTY_CACHE_HASH(obj, id) \
    ((((jsuword)(obj) >> JSVAL_TAGBITS) ^ (jsuword)(id)) & PROPERTY_CACHE_MASK)

#ifdef JS_THREADSAFE

#if HAVE_ATOMIC_DWORD_ACCESS

#define PCE_LOAD(cache, pce, entry)     JS_ATOMIC_DWORD_LOAD(pce, entry)
#define PCE_STORE(cache, pce, entry)    JS_ATOMIC_DWORD_STORE(pce, entry)

#else  /* !HAVE_ATOMIC_DWORD_ACCESS */

#define PCE_LOAD(cache, pce, entry)                                           \
    JS_BEGIN_MACRO                                                            \
	uint32 _prefills;                                                     \
	uint32 _fills = (cache)->fills;                                       \
	do {                                                                  \
	    /* Load until cache->fills is stable (see FILL macro below). */   \
	    _prefills = _fills;                                               \
	    (entry) = *(pce);                                                 \
	} while ((_fills = (cache)->fills) != _prefills);                     \
    JS_END_MACRO

#define PCE_STORE(cache, pce, entry)                                          \
    JS_BEGIN_MACRO                                                            \
	do {                                                                  \
	    /* Store until no racing collider stores half or all of pce. */   \
	    *(pce) = (entry);                                                 \
	} while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) ||                     \
		 PCE_PROPERTY(*pce) != PCE_PROPERTY(entry));                  \
    JS_END_MACRO

#endif /* !HAVE_ATOMIC_DWORD_ACCESS */

#else  /* !JS_THREADSAFE */

#define PCE_LOAD(cache, pce, entry)     ((entry) = *(pce))
#define PCE_STORE(cache, pce, entry)    (*(pce) = (entry))

#endif /* !JS_THREADSAFE */

typedef union JSPropertyCacheEntry {
    struct {
	JSObject    *object;    /* weak link to object */
	JSProperty  *property;  /* weak link to property, or not-found id */
    } s;
#ifdef HAVE_ATOMIC_DWORD_ACCESS
    prdword align;
#endif
} JSPropertyCacheEntry;

/* These may be called in lvalue or rvalue position. */
#define PCE_OBJECT(entry)       ((entry).s.object)
#define PCE_PROPERTY(entry)     ((entry).s.property)

typedef struct JSPropertyCache {
    JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE];
    JSBool               empty;
    uint32               fills;
    uint32               recycles;
    uint32               tests;
    uint32               misses;
    uint32               flushes;
} JSPropertyCache;

/* Property-not-found lookup results are cached using this invalid pointer. */
#define PROP_NOT_FOUND(obj,id)  ((JSProperty *) ((jsword)(id) | 1))
#define PROP_NOT_FOUND_ID(prop) ((jsid) ((jsword)(prop) & ~1))
#define PROP_FOUND(prop)        ((prop) && ((jsword)(prop) & 1) == 0)

#define PROPERTY_CACHE_FILL(cx, cache, obj, id, prop)                         \
    JS_BEGIN_MACRO                                                            \
	uintN _hashIndex = (uintN)PROPERTY_CACHE_HASH(obj, id);               \
	JSPropertyCache *_cache = (cache);                                    \
	JSPropertyCacheEntry *_pce = &_cache->table[_hashIndex];              \
	JSPropertyCacheEntry _entry;                                          \
	JSProperty *_pce_prop;                                                \
	PCE_LOAD(_cache, _pce, _entry);                                       \
	_pce_prop = PCE_PROPERTY(_entry);                                     \
	if (_pce_prop && _pce_prop != prop)                                   \
	    _cache->recycles++;                                               \
	PCE_OBJECT(_entry) = obj;                                             \
	PCE_PROPERTY(_entry) = prop;                                          \
	_cache->empty = JS_FALSE;                                             \
	_cache->fills++;                                                      \
	PCE_STORE(_cache, _pce, _entry);                                      \
    JS_END_MACRO

#define PROPERTY_CACHE_TEST(cache, obj, id, prop)                             \
    JS_BEGIN_MACRO                                                            \
	uintN _hashIndex = (uintN)PROPERTY_CACHE_HASH(obj, id);               \
	JSPropertyCache *_cache = (cache);                                    \
	JSPropertyCacheEntry *_pce = &_cache->table[_hashIndex];              \
	JSPropertyCacheEntry _entry;                                          \
	JSProperty *_pce_prop;                                                \
	PCE_LOAD(_cache, _pce, _entry);                                       \
	_pce_prop = PCE_PROPERTY(_entry);                                     \
	_cache->tests++;                                                      \
	if (_pce_prop &&                                                      \
	    (((jsword)_pce_prop & 1)                                          \
	     ? PROP_NOT_FOUND_ID(_pce_prop)                                   \
	     : sym_id(((JSScopeProperty *)_pce_prop)->symbols)) == id &&      \
	    PCE_OBJECT(_entry) == obj) {                                      \
	    prop = _pce_prop;                                                 \
	} else {                                                              \
	    _cache->misses++;                                                 \
	    prop = NULL;                                                      \
	}                                                                     \
    JS_END_MACRO

extern void
js_FlushPropertyCache(JSContext *cx);

extern void
js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop);

extern jsval *
js_AllocStack(JSContext *cx, uintN nslots, void **markp);

extern void
js_FreeStack(JSContext *cx, void *mark);

extern JSBool
js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_Invoke(JSContext *cx, uintN argc, JSBool constructing);

extern JSBool
js_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval,
		     uintN argc, jsval *argv, jsval *rval);

extern JSBool
js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun,
	   JSStackFrame *down, JSBool debugging, jsval *result);

extern JSBool
js_Interpret(JSContext *cx, jsval *result);

#if XP_MAC //AJFMOD
typedef JSBool (*EnumDestroyerProcPtr)(jsval iter_state);

extern void js_SetFwEnumDestroyer(EnumDestroyerProcPtr destroyer);

#endif
#endif /* jsinterp_h___ */

**** End of jsinterp.h. ****

**** Start of jslibmath.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * By default all math calls go to fdlibm.  The defines for each platform
 * remap the math calls to native routines.
 */

#ifndef _LIBMATH_H
#define _LIBMATH_H

#include <math.h>
#include "jsconfig.h"

/*
 * Define which platforms on which to use fdlibm.  Not used
 * by default since there can be problems with endian-ness and such.
 */

#if defined(_WIN32)
#define JS_USE_FDLIBM_MATH 1

#elif defined(SUNOS4)
#define JS_USE_FDLIBM_MATH 1

#elif defined(IRIX)
#define JS_USE_FDLIBM_MATH 1

#elif defined(SOLARIS)
#define JS_USE_FDLIBM_MATH 1

#elif defined(HPUX)
#define JS_USE_FDLIBM_MATH 1

#elif defined(linux)
#define JS_USE_FDLIBM_MATH 1

#elif defined(OSF1)
/* Want to use some fdlibm functions but fdlibm broken on OSF1/alpha. */
#define JS_USE_FDLIBM_MATH 0

#elif defined(AIX)
#define JS_USE_FDLIBM_MATH 1

#else
#define JS_USE_FDLIBM_MATH 0
#endif

#if !JS_USE_FDLIBM_MATH

/*
 * Use system provided math routines.
 */

#define fd_acos acos
#define fd_asin asin
#define fd_atan atan
#define fd_atan2 atan2
#define fd_ceil ceil
#define fd_copysign copysign
#define fd_cos cos
#define fd_exp exp
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod
#define fd_log log
#define fd_pow pow
#define fd_sin sin
#define fd_sqrt sqrt
#define fd_tan tan

#else

/*
 * Use math routines in fdlibm.
 */

#ifdef __STDC__
#define __P(p)  p
#else
#define __P(p)  ()
#endif

#if defined _WIN32 || defined SUNOS4 

#define fd_acos acos
#define fd_asin asin
#define fd_atan atan
#define fd_cos cos
#define fd_sin sin
#define fd_tan tan
#define fd_exp exp
#define fd_log log
#define fd_sqrt sqrt
#define fd_ceil ceil
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod

extern double fd_atan2 __P((double, double));
extern double fd_copysign __P((double, double));

#ifdef DEBUG
extern double fd_pow __P((double, double));
#else
#define fd_pow pow
#endif

#elif defined IRIX

#define fd_acos acos
#define fd_asin asin
#define fd_atan atan
#define fd_exp exp
#define fd_log log
#define fd_log10 log10
#define fd_sqrt sqrt
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod

extern double fd_cos __P((double));
extern double fd_sin __P((double));
extern double fd_tan __P((double));
extern double fd_atan2 __P((double, double));
extern double fd_pow __P((double, double));
extern double fd_ceil __P((double));
extern double fd_copysign __P((double, double));

#elif defined SOLARIS

#define fd_atan atan
#define fd_cos cos
#define fd_sin sin
#define fd_tan tan
#define fd_exp exp
#define fd_sqrt sqrt
#define fd_ceil ceil
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod

extern double fd_acos __P((double));
extern double fd_asin __P((double));
extern double fd_log __P((double));
extern double fd_atan2 __P((double, double));
extern double fd_pow __P((double, double));
extern double fd_copysign __P((double, double));

#elif defined HPUX

#define fd_cos cos
#define fd_sin sin
#define fd_exp exp
#define fd_sqrt sqrt
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod

extern double fd_ceil __P((double));
extern double fd_acos __P((double));
extern double fd_log __P((double));
extern double fd_atan2 __P((double, double));
extern double fd_tan __P((double));
extern double fd_pow __P((double, double));
extern double fd_asin __P((double));
extern double fd_atan __P((double));
extern double fd_copysign __P((double, double));

#elif defined(linux)

#define fd_atan atan
#define fd_atan2 atan2
#define fd_ceil ceil
#define fd_cos cos
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod
#define fd_sin sin
#define fd_sqrt sqrt
#define fd_tan tan

extern double fd_asin __P((double));
extern double fd_acos __P((double));
extern double fd_exp __P((double));
extern double fd_log __P((double));
extern double fd_pow __P((double, double));
extern double fd_copysign __P((double, double));

#elif defined(OSF1)

#define fd_acos acos
#define fd_asin asin
#define fd_atan atan
#define fd_copysign copysign
#define fd_cos cos
#define fd_exp exp
#define fd_fabs fabs
#define fd_fmod fmod
#define fd_sin sin
#define fd_sqrt sqrt
#define fd_tan tan

extern double fd_atan2 __P((double, double));
extern double fd_ceil __P((double));
extern double fd_floor __P((double));
extern double fd_log __P((double));
extern double fd_pow __P((double, double));

#elif defined(AIX)

#define fd_acos acos
#define fd_asin asin
#define fd_atan2 atan2
#define fd_copysign copysign
#define fd_cos cos
#define fd_exp exp
#define fd_fabs fabs
#define fd_floor floor
#define fd_fmod fmod
#define fd_log log
#define fd_sin sin
#define fd_sqrt sqrt

extern double fd_atan __P((double));
extern double fd_ceil __P((double));
extern double fd_pow __P((double,double));
extern double fd_tan __P((double));

#else /* other platform.. generic paranoid slow fdlibm */

extern double fd_acos __P((double));
extern double fd_asin __P((double));
extern double fd_atan __P((double));
extern double fd_cos __P((double));
extern double fd_sin __P((double));
extern double fd_tan __P((double));
 
extern double fd_exp __P((double));
extern double fd_log __P((double));
extern double fd_sqrt __P((double));

extern double fd_ceil __P((double));
extern double fd_fabs __P((double));
extern double fd_floor __P((double));
extern double fd_fmod __P((double, double));

extern double fd_atan2 __P((double, double));
extern double fd_pow __P((double, double));
extern double fd_copysign __P((double, double));

#endif

#endif /* JS_USE_FDLIBM_MATH */

#endif /* _LIBMATH_H */


**** End of jslibmath.h. ****

**** Start of jslock.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifdef JS_THREADSAFE

/*
 * JS locking stubs.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include "jspubtd.h"
#include "prthread.h"
#include "pratom.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jscntxt.h"
#include "jsscope.h"
#include "jspubtd.h"
#include "jslock.h"

static PRLock *_global_lock;

static void
js_LockGlobal()
{
  PR_Lock(_global_lock);
}

static void
js_UnlockGlobal()
{
  PR_Unlock(_global_lock);
}

#define ReadWord(W) (W)
#define AtomicAddBody(P,I)\
    jsword n;\
    do {\
	n = ReadWord(*(P));\
    } while (!js_CompareAndSwap(P, n, n + I));

#if defined(_WIN32) && !defined(NSPR_LOCK)
#pragma warning( disable : 4035 )

JS_INLINE int
js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
{
    __asm {
		mov eax,ov
		mov ecx, nv
		mov ebx, w
		lock cmpxchg [ebx], ecx
		sete al
		and eax,1h
	}
}

#elif defined(SOLARIS) && !defined(NSPR_LOCK)

#ifndef ULTRA_SPARC
JS_INLINE jsword
js_ReadWord(jsword *p)
{
    jsword n;
    while ((n = *p) == -1)
	;
    return n;
}

#undef ReadWord
#define ReadWord(W) js_ReadWord(&(W))

static PRLock *_counter_lock;
#define UsingCounterLock 1

#undef AtomicAddBody
#define AtomicAddBody(P,I) \
    PR_Lock(_counter_lock);\
    *(P) += I;\
    PR_Unlock(_counter_lock);

#endif /* !ULTRA_SPARC */

JS_INLINE int
js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
{
#if defined(__GNUC__)
    unsigned int res;
#ifndef ULTRA_SPARC
    JS_ASSERT(nv != -1);
    asm volatile ("\
stbar\n\
swap [%1],%4\n\
1:  cmp %4,-1\n\
be,a 1b\n\
swap [%1],%4\n\
cmp %2,%4\n\
be,a 2f\n\
swap [%1],%3\n\
swap [%1],%4\n\
ba 3f\n\
mov 0,%0\n\
2:  mov 1,%0\n\
3:"
		  : "=r" (res)
		  : "r" (w), "r" (ov), "r" (nv), "r" (-1));
#else /* ULTRA_SPARC */
    JS_ASSERT(ov != nv);
    asm volatile ("\
stbar\n\
cas [%1],%2,%3\n\
cmp %2,%3\n\
be,a 1f\n\
mov 1,%0\n\
mov 0,%0\n\
1:"
		  : "=r" (res)
		  : "r" (w), "r" (ov), "r" (nv));
#endif /* ULTRA_SPARC */
    return (int)res;
#else /* !__GNUC__ */
    extern int compare_and_swap(jsword*,jsword,jsword);
#ifndef ULTRA_SPARC
    JS_ASSERT(nv != -1);
#else
    JS_ASSERT(ov != nv);
#endif
    return compare_and_swap(w,ov,nv);
#endif
}

#elif defined(AIX) && !defined(NSPR_LOCK)
#include <sys/atomic_op.h>

JS_INLINE int
js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
{
    return !_check_lock(w,ov,nv);
}

#else

static PRLock *_counter_lock;
#define UsingCounterLock 1

#undef AtomicAddBody
#define AtomicAddBody(P,I) \
    PR_Lock(_counter_lock);\
    *(P) += I;\
    PR_Unlock(_counter_lock);

static PRLock *_compare_and_swap_lock;
#define UsingCompareAndSwapLock 1

JS_INLINE int
js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
{
    int res = 0;

    PR_Lock(_compare_and_swap_lock);
    if (*w == ov) {
      *w = nv;
      res = 1;
    }
    PR_Unlock(_compare_and_swap_lock);
    return res;
}

#endif /* arch-tests */

JS_INLINE void
js_AtomicAdd(jsword *p, jsword i)
{
    AtomicAddBody(p,i);
}

static JS_INLINE jsword
js_AtomicSet(jsword *p, jsword n)
{
    jsword o;
    do {
	o = ReadWord(*p);
    } while (!js_CompareAndSwap(p,o,n));
    return o;
}

jsword
js_CurrentThreadId()
{
    return CurrentThreadId();
}

void
js_NewLock(JSThinLock *p)
{
#ifdef NSPR_LOCK
    p->owner = 0;
    p->fat = (JSFatLock*)JS_NEW_LOCK();
#else
    memset(p, 0, sizeof(JSThinLock));
#endif
}

void
js_DestroyLock(JSThinLock *p)
{
#ifdef NSPR_LOCK
    p->owner = 0xdeadbeef;
    JS_DESTROY_LOCK(((JSLock*)p->fat));
#else
    JS_ASSERT(p->owner == 0);
    JS_ASSERT(p->fat == NULL);
#endif
}

static void js_Dequeue(JSThinLock *);

JS_INLINE jsval
js_GetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot)
{
    jsval v;
#ifndef NSPR_LOCK
    JSScope *scp = (JSScope *)obj->map;
    JSThinLock *p = &scp->lock;
    jsword me = cx->thread;
#endif

    JS_ASSERT(obj->slots && slot < obj->map->freeslot);
#ifndef NSPR_LOCK
    JS_ASSERT(me == CurrentThreadId());
    if (js_CompareAndSwap(&p->owner, 0, me)) {
	if (scp == (JSScope *)obj->map) {
	    v = obj->slots[slot];
	    if (!js_CompareAndSwap(&p->owner, me, 0)) {
		scp->count = 1;
		js_UnlockObj(cx,obj);
	    }
	    return v;
	}
	if (!js_CompareAndSwap(&p->owner, me, 0))
	    js_Dequeue(p);
    }
    else if (Thin_RemoveWait(ReadWord(p->owner)) == me) {
	return obj->slots[slot];
    }
#endif
    js_LockObj(cx,obj);
    v = obj->slots[slot];
    js_UnlockObj(cx,obj);
    return v;
}

JS_INLINE void
js_SetSlotWhileLocked(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
{
#ifndef NSPR_LOCK
    JSScope *scp = (JSScope *)obj->map;
    JSThinLock *p = &scp->lock;
    jsword me = cx->thread;
#endif

    JS_ASSERT(obj->slots && slot < obj->map->freeslot);
#ifndef NSPR_LOCK
    JS_ASSERT(me == CurrentThreadId());
    if (js_CompareAndSwap(&p->owner, 0, me)) {
	if (scp == (JSScope *)obj->map) {
	    obj->slots[slot] = v;
	    if (!js_CompareAndSwap(&p->owner, me, 0)) {
		scp->count = 1;
		js_UnlockObj(cx,obj);
	    }
	    return;
	}
	if (!js_CompareAndSwap(&p->owner, me, 0))
	    js_Dequeue(p);
    }
    else if (Thin_RemoveWait(ReadWord(p->owner)) == me) {
	obj->slots[slot] = v;
	return;
    }
#endif
    js_LockObj(cx,obj);
    obj->slots[slot] = v;
    js_UnlockObj(cx,obj);
}

static JSFatLock *
mallocFatlock()
{
    JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */
    JS_ASSERT(fl);
    fl->susp = 0;
    fl->next = NULL;
    fl->prev = NULL;
    fl->slock = PR_NewLock();
    fl->svar = PR_NewCondVar(fl->slock);
    return fl;
}

static void
freeFatlock(JSFatLock *fl)
{
    PR_DestroyLock(fl->slock);
    PR_DestroyCondVar(fl->svar);
    free(fl);
}

static int
js_SuspendThread(JSThinLock *p)
{
    JSFatLock *fl;
    JSStatus stat;

    while ((fl = (JSFatLock*)js_AtomicSet((jsword*)&p->fat,1)) == (JSFatLock*)1) /* busy wait */
	PR_Sleep(PR_INTERVAL_NO_WAIT);
    if (fl == NULL)
	return 1;
    PR_Lock(fl->slock);
    js_AtomicSet((jsword*)&p->fat,(jsword)fl);
    fl->susp++;
    if (fl->susp < 1) {
	PR_Unlock(fl->slock);
	return 1;
    }
    stat = PR_WaitCondVar(fl->svar,PR_INTERVAL_NO_TIMEOUT);
    if (stat == JS_FAILURE) {
	fl->susp--;
	return 0;
    }
    PR_Unlock(fl->slock);
    return 1;
}

static void
js_ResumeThread(JSThinLock *p)
{
    JSFatLock *fl;
    JSStatus stat;

    while ((fl = (JSFatLock*)js_AtomicSet((jsword*)&p->fat,1)) == (JSFatLock*)1)
	PR_Sleep(PR_INTERVAL_NO_WAIT);
    if (fl == NULL)
	return;
    PR_Lock(fl->slock);
    js_AtomicSet((jsword*)&p->fat,(jsword)fl);
    fl->susp--;
    if (fl->susp < 0) {
	PR_Unlock(fl->slock);
	return;
    }
    stat = PR_NotifyCondVar(fl->svar);
    JS_ASSERT(stat != JS_FAILURE);
    PR_Unlock(fl->slock);
}

static JSFatLock *
listOfFatlocks(int l)
{
    JSFatLock *m;
    JSFatLock *m0;
	int i;

    JS_ASSERT(l>0);
    m0 = m = mallocFatlock();
    for (i=1; i<l; i++) {
		m->next = mallocFatlock();
		m = m->next;
    }
    return m0;
}

static void
deleteListOfFatlocks(JSFatLock *m)
{
    JSFatLock *m0;
    for (; m; m=m0) {
	m0 = m->next;
	freeFatlock(m);
    }
}

static JSFatLockTable _fl_table;

static JSFatLock *
allocateFatlock()
{
  JSFatLock *m;

  js_LockGlobal();
  if (_fl_table.free == NULL) {
#ifdef DEBUG
      printf("Ran out of fat locks!\n");
#endif
      _fl_table.free = listOfFatlocks(10);
  }
  m = _fl_table.free;
  _fl_table.free = m->next;
  _fl_table.free->prev = NULL;
  m->susp = 0;
  m->next = _fl_table.taken;
  m->prev = NULL;
  if (_fl_table.taken != NULL)
	_fl_table.taken->prev = m;
  _fl_table.taken = m;
  js_UnlockGlobal();
  return m;
}

static void
deallocateFatlock(JSFatLock *m)
{
  if (m == NULL)
	return;
  js_LockGlobal();
  if (m->prev != NULL)
	m->prev->next = m->next;
  if (m->next != NULL)
	m->next->prev = m->prev;
  if (m == _fl_table.taken)
	_fl_table.taken = m->next;
  m->next = _fl_table.free;
  _fl_table.free = m;
  js_UnlockGlobal();
}

int
js_SetupLocks(int l)
{
  if (l > 10000)       /* l equals number of initially allocated fat locks */
#ifdef DEBUG
    printf("Number %d very large in js_SetupLocks()!\n",l);
#endif
  if (_global_lock)
    return 1;
  _global_lock = PR_NewLock();
  JS_ASSERT(_global_lock);
#ifdef UsingCounterLock
  _counter_lock = PR_NewLock();
  JS_ASSERT(_counter_lock);
#endif
#ifdef UsingCompareAndSwapLock
  _compare_and_swap_lock = PR_NewLock();
  JS_ASSERT(_compare_and_swap_lock);
#endif
  _fl_table.free = listOfFatlocks(l);
  _fl_table.taken = NULL;
  return 1;
}

void
js_CleanupLocks()
{
  if (_global_lock != NULL) {
    deleteListOfFatlocks(_fl_table.free);
    _fl_table.free = NULL;
    deleteListOfFatlocks(_fl_table.taken);
    _fl_table.taken = NULL;
    PR_DestroyLock(_global_lock);
    _global_lock = NULL;
#ifdef UsingCounterLock
    PR_DestroyLock(_counter_lock);
    _counter_lock = NULL;
#endif
#ifdef UsingCompareAndSwapLock
    PR_DestroyLock(_compare_and_swap_lock);
    _compare_and_swap_lock = NULL;
#endif
  }
}

JS_PUBLIC_API(void)
js_InitContextForLocking(JSContext *cx)
{
	cx->thread = CurrentThreadId();
	JS_ASSERT(Thin_GetWait(cx->thread) == 0);
}

/*

  It is important that emptyFatlock() clears p->fat in the empty case
  while holding p->slock. This serializes the access of p->fat wrt
  js_SuspendThread(), which also requires p->slock.  There would
  otherwise be a race condition as follows.  Thread A is about to
  clear p->fat, having woken up after being suspended in a situation
  where no other thread has yet suspended on p. However, suppose
  thread B is about to suspend on p, having just released the global
  lock, and is currently calling js_SuspendThread(). In the unfortunate
  case where A precedes B ever so slightly, A will think that it
  should clear p->fat (being currently empty but not for long), at the
  same time as B is about to suspend on p->fat.  Now, A deallocates
  p->fat while B is about to suspend on it. Thus, the suspension of B
  is lost and B will not be properly activated.

  Using p->slock as below (and correspondingly in js_SuspendThread()),
  js_SuspendThread() will notice that p->fat is empty, and hence return
  immediately.

  */

static int
emptyFatlock(JSThinLock *p)
{
    JSFatLock *fl;
    int i;
    PRLock* lck;

    while ((fl = (JSFatLock*)js_AtomicSet((jsword*)&p->fat,1)) == (JSFatLock*)1)
       PR_Sleep(PR_INTERVAL_NO_WAIT);
    if (fl == NULL) {
	js_AtomicSet((jsword*)&p->fat,(jsword)fl);
	return 1;
    }
    lck = fl->slock;
    PR_Lock(lck);
    i = fl->susp;
    if (i < 1) {
	fl->susp = -1;
	deallocateFatlock(fl);
	fl = NULL;
    }
    js_AtomicSet((jsword*)&p->fat,(jsword)fl);
    PR_Unlock(lck);
    return i < 1;
}

/*
  Fast locking and unlocking is implemented by delaying the
  allocation of a system lock (fat lock) until contention. As long as
  a locking thread A runs uncontended, the lock is represented solely
  by storing A's identity in the object being locked.

  If another thread B tries to lock the object currently locked by A,
  B is enqueued into a fat lock structure (which might have to be
  allocated and pointed to by the object), and suspended using NSPR
  conditional variables (wait).  A wait bit (Bacon bit) is set in the
  lock word of the object, signalling to A that when releasing the
  lock, B must be dequeued and notified.

  The basic operation of the locking primitives (js_Lock(),
  js_Unlock(), js_Enqueue(), and js_Dequeue()) is
  compare-and-swap. Hence, when locking into p, if
  compare-and-swap(p,NULL,me) succeeds this implies that p is
  unlocked.  Similarly, when unlocking p, if
  compare-and-swap(p,me,NULL) succeeds this implies that p is
  uncontended (no one is waiting because the wait bit is not set).

  Furthermore, when enqueueing (after the compare-and-swap has failed
  to lock the object), p->fat is used to serialize the different
  accesses to the fat lock. The four function thus synchronized are
  js_Enqueue, emptyFatLock, js_SuspendThread, and js_ResumeThread.

  When dequeueing, the lock is released, and one of the threads
  suspended on the lock is notified.  If other threads still are
  waiting, the wait bit is kept (in js_Enqueue), and if not, the fat
  lock is deallocated (in emptyFatlock()).

  p->fat is set to 1 by enqueue and emptyFatlock to signal that the pointer
  is being accessed.

*/

static void
js_Enqueue(JSThinLock *p, jsword me)
{
    jsword o, n;

    while (1) {
	o = ReadWord(p->owner);
	n = Thin_SetWait(o);
	if (o != 0 && js_CompareAndSwap(&p->owner,o,n)) {
	    JSFatLock* fl;
	    while ((fl = (JSFatLock*)js_AtomicSet((jsword*)&p->fat,1)) == (JSFatLock*)1)
		PR_Sleep(PR_INTERVAL_NO_WAIT);
	    if (fl == NULL)
		fl = allocateFatlock();
	    js_AtomicSet((jsword*)&p->fat,(jsword)fl);
	    js_SuspendThread(p);
	    if (emptyFatlock(p))
		me = Thin_RemoveWait(me);
	    else
		me = Thin_SetWait(me);
	}
	else if (js_CompareAndSwap(&p->owner,0,me)) {
	    return;
	}
    }
}

static void
js_Dequeue(JSThinLock *p)
{
    int o = ReadWord(p->owner);
    JS_ASSERT(Thin_GetWait(o));
    if (!js_CompareAndSwap(&p->owner,o,0)) /* release it */
	JS_ASSERT(0);
    js_ResumeThread(p);
}

JS_INLINE void
js_Lock(JSThinLock *p, jsword me)
{
    JS_ASSERT(me == CurrentThreadId());
    if (js_CompareAndSwap(&p->owner, 0, me))
	return;
    if (Thin_RemoveWait(ReadWord(p->owner)) != me)
	js_Enqueue(p, me);
#ifdef DEBUG
    else
	JS_ASSERT(0);
#endif
}

JS_INLINE void
js_Unlock(JSThinLock *p, jsword me)
{
    JS_ASSERT(me == CurrentThreadId());
    if (js_CompareAndSwap(&p->owner, me, 0))
	return;
    if (Thin_RemoveWait(ReadWord(p->owner)) == me)
	js_Dequeue(p);
#ifdef DEBUG
    else
	JS_ASSERT(0);
#endif
}

void
js_LockRuntime(JSRuntime *rt)
{
    jsword me = CurrentThreadId();
    JSThinLock *p;

    JS_ASSERT(Thin_RemoveWait(ReadWord(rt->rtLock.owner)) != me);
    p = &rt->rtLock;
    JS_LOCK0(p,me);
}

void
js_UnlockRuntime(JSRuntime *rt)
{
    jsword me = CurrentThreadId();
    JSThinLock *p;

    JS_ASSERT(Thin_RemoveWait(ReadWord(rt->rtLock.owner)) == me);
    p = &rt->rtLock;
    JS_UNLOCK0(p,me);
}

static JS_INLINE void
js_LockScope1(JSContext *cx, JSScope *scope, jsword me)
{
    JSThinLock *p;

    if (Thin_RemoveWait(ReadWord(scope->lock.owner)) == me) {
	JS_ASSERT(scope->count > 0);
	scope->count++;
    } else {
	p = &scope->lock;
	JS_LOCK0(p,me);
	JS_ASSERT(scope->count == 0);
	scope->count = 1;
    }
}

void
js_LockScope(JSContext *cx, JSScope *scope)
{
    JS_ASSERT(cx->thread == CurrentThreadId());
    js_LockScope1(cx,scope,cx->thread);
}

void
js_UnlockScope(JSContext *cx, JSScope *scope)
{
    jsword me = cx->thread;
    JSThinLock *p;

    JS_ASSERT(scope->count > 0);
    if (Thin_RemoveWait(ReadWord(scope->lock.owner)) != me) {
	JS_ASSERT(0);
	return;
    }
    if (--scope->count == 0) {
	p = &scope->lock;
	JS_UNLOCK0(p,me);
    }
}

void
js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope)
{
    jsword me;
    JSThinLock *p;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(newscope));
    /*
     * If the last reference to oldscope went away, newscope needs no lock
     * state update.
     */
    if (!oldscope) {
	return;
    }
    JS_ASSERT(JS_IS_SCOPE_LOCKED(oldscope));
    /*
     * Transfer oldscope's entry count to newscope, as it will be unlocked
     * now via JS_UNLOCK_OBJ(cx,obj) calls made while we unwind the C stack
     * from the current point (under js_GetMutableScope).
     */
    newscope->count = oldscope->count;
    /*
     * Reset oldscope's lock state so that it is completely unlocked.
     */
    oldscope->count = 0;
    p = &oldscope->lock;
    me = cx->thread;
    JS_UNLOCK0(p,me);
}

void
js_LockObj(JSContext *cx, JSObject *obj)
{
    JSScope *scope;
    jsword me = cx->thread;
    JS_ASSERT(me == CurrentThreadId());
    for (;;) {
		scope = (JSScope *) obj->map;
		js_LockScope1(cx, scope, me);

		/* If obj still has this scope, we're done. */
		if (scope == (JSScope *) obj->map)
			return;

		/* Lost a race with a mutator; retry with obj's new scope. */
		js_UnlockScope(cx, scope);
    }
}

void
js_UnlockObj(JSContext *cx, JSObject *obj)
{
    js_UnlockScope(cx, (JSScope *) obj->map);
}

#ifdef DEBUG
JSBool
js_IsRuntimeLocked(JSRuntime *rt)
{
    return CurrentThreadId() == Thin_RemoveWait(ReadWord(rt->rtLock.owner));
}

JSBool
js_IsObjLocked(JSObject *obj)
{
    JSObjectMap *map = obj->map;

    return MAP_IS_NATIVE(map) && CurrentThreadId() == Thin_RemoveWait(ReadWord(((JSScope *)map)->lock.owner));
}

JSBool
js_IsScopeLocked(JSScope *scope)
{
    return CurrentThreadId() == Thin_RemoveWait(ReadWord(scope->lock.owner));
}
#endif

#undef ReadWord
#endif /* JS_THREADSAFE */

**** End of jslock.c. ****

**** Start of jslock.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
#ifndef jslock_h__
#define jslock_h__

#ifdef JS_THREADSAFE

#include "jstypes.h"
#include "prlock.h"
#include "prcvar.h"
#include "jshash.h" /* Added by JSIFY */

#define Thin_GetWait(W) ((jsword)(W) & 0x1)
#define Thin_SetWait(W) ((jsword)(W) | 0x1)
#define Thin_RemoveWait(W) ((jsword)(W) & ~0x1)

typedef struct JSFatLock {
    int susp;
    PRLock* slock;
    PRCondVar* svar;
    struct JSFatLock *next;
    struct JSFatLock *prev;
} JSFatLock;

typedef struct JSThinLock {
  jsword owner;
  JSFatLock *fat;
} JSThinLock;

typedef PRLock JSLock;

typedef struct JSFatLockTable {
    JSFatLock *free;
    JSFatLock *taken;
} JSFatLockTable;

#define JS_ATOMIC_ADDREF(p, i) js_AtomicAdd(p,i)

#define CurrentThreadId() (jsword)PR_GetCurrentThread()
#define JS_CurrentThreadId() js_CurrentThreadId()
#define JS_NEW_LOCK()               PR_NewLock()
#define JS_DESTROY_LOCK(l)          PR_DestroyLock(l)
#define JS_ACQUIRE_LOCK(l)          PR_Lock(l)
#define JS_RELEASE_LOCK(l)          PR_Unlock(l)
#define JS_LOCK0(P,M) js_Lock(P,M)
#define JS_UNLOCK0(P,M) js_Unlock(P,M)

#define JS_NEW_CONDVAR(l)           PR_NewCondVar(l)
#define JS_DESTROY_CONDVAR(cv)      PR_DestroyCondVar(cv)
#define JS_WAIT_CONDVAR(cv,to)      PR_WaitCondVar(cv,to)
#define JS_NO_TIMEOUT               PR_INTERVAL_NO_TIMEOUT
#define JS_NOTIFY_CONDVAR(cv)       PR_NotifyCondVar(cv)
#define JS_NOTIFY_ALL_CONDVAR(cv)   PR_NotifyAllCondVar(cv)

#ifdef DEBUG
#include "jsscope.h"

#define _SET_OBJ_INFO(obj,f,l)                                                \
    _SET_SCOPE_INFO(((JSScope*)obj->map),f,l)

#define _SET_SCOPE_INFO(scope,f,l)                                            \
    (JS_ASSERT(scope->count > 0 && scope->count <= 4),                        \
     scope->file[scope->count-1] = f,                                         \
     scope->line[scope->count-1] = l)
#endif /* DEBUG */

#define JS_LOCK_RUNTIME(rt)         js_LockRuntime(rt)
#define JS_UNLOCK_RUNTIME(rt)       js_UnlockRuntime(rt)
#define JS_LOCK_OBJ(cx,obj)         (js_LockObj(cx, obj),                    \
				      _SET_OBJ_INFO(obj,__FILE__,__LINE__))
#define JS_UNLOCK_OBJ(cx,obj)       js_UnlockObj(cx, obj)
#define JS_LOCK_SCOPE(cx,scope)     (js_LockScope(cx, scope),                \
				      _SET_SCOPE_INFO(scope,__FILE__,__LINE__))
#define JS_UNLOCK_SCOPE(cx,scope)   js_UnlockScope(cx, scope)
#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) js_TransferScopeLock(cx, scope, newscope)

extern jsword js_CurrentThreadId();
extern JS_INLINE void js_Lock(JSThinLock *, jsword);
extern JS_INLINE void js_Unlock(JSThinLock *, jsword);
extern int js_CompareAndSwap(jsword *, jsword, jsword);
extern void js_AtomicAdd(jsword*, jsword);
extern void js_LockRuntime(JSRuntime *rt);
extern void js_UnlockRuntime(JSRuntime *rt);
extern void js_LockObj(JSContext *cx, JSObject *obj);
extern void js_UnlockObj(JSContext *cx, JSObject *obj);
extern void js_LockScope(JSContext *cx, JSScope *scope);
extern void js_UnlockScope(JSContext *cx, JSScope *scope);
extern int js_SetupLocks(int);
extern void js_CleanupLocks();
extern JS_PUBLIC_API(void) js_InitContextForLocking(JSContext *);
extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *);
extern JS_PUBLIC_API(jsval) js_GetSlotWhileLocked(JSContext *, JSObject *, uint32);
extern JS_PUBLIC_API(void) js_SetSlotWhileLocked(JSContext *, JSObject *, uint32, jsval);
extern void js_NewLock(JSThinLock *);
extern void js_DestroyLock(JSThinLock *);

#ifdef DEBUG

#define JS_IS_RUNTIME_LOCKED(rt)    js_IsRuntimeLocked(rt)
#define JS_IS_OBJ_LOCKED(obj)       js_IsObjLocked(obj)
#define JS_IS_SCOPE_LOCKED(scope)   js_IsScopeLocked(scope)

extern JSBool js_IsRuntimeLocked(JSRuntime *rt);
extern JSBool js_IsObjLocked(JSObject *obj);
extern JSBool js_IsScopeLocked(JSScope *scope);

#else

#define JS_IS_RUNTIME_LOCKED(rt)    0
#define JS_IS_OBJ_LOCKED(obj)       1
#define JS_IS_SCOPE_LOCKED(scope)   1

#endif /* DEBUG */

#define JS_LOCK_OBJ_VOID(cx, obj, e)                                          \
    JS_BEGIN_MACRO                                                            \
	js_LockObj(cx, obj);                                                 \
	e;                                                                    \
	js_UnlockObj(cx, obj);                                               \
    JS_END_MACRO

#define JS_LOCK_VOID(cx, e)                                                   \
    JS_BEGIN_MACRO                                                            \
	JSRuntime *_rt = (cx)->runtime;                                       \
	JS_LOCK_RUNTIME_VOID(_rt, e);                                         \
    JS_END_MACRO

#if defined(JS_USE_ONLY_NSPR_LOCKS) || !(defined(_WIN32) || defined(SOLARIS) || defined(AIX))

#undef JS_LOCK0
#undef JS_UNLOCK0
#define JS_LOCK0(P,M) JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)); (P)->owner = (M)
#define JS_UNLOCK0(P,M) (P)->owner = 0; JS_RELEASE_LOCK(((JSLock*)(P)->fat))
#define NSPR_LOCK 1

#endif /* arch-tests */

#else  /* !JS_THREADSAFE */

#define JS_ATOMIC_ADDREF(p,i)       (*(p) += i)

#define JS_CurrentThreadId() 0
#define JS_NEW_LOCK()               NULL
#define JS_DESTROY_LOCK(l)          ((void)0)
#define JS_ACQUIRE_LOCK(l)          ((void)0)
#define JS_RELEASE_LOCK(l)          ((void)0)
#define JS_LOCK0(P,M)                ((void)0)
#define JS_UNLOCK0(P,M)              ((void)0)

#define JS_NEW_CONDVAR(l)           NULL
#define JS_DESTROY_CONDVAR(cv)      ((void)0)
#define JS_WAIT_CONDVAR(cv,to)      ((void)0)
#define JS_NOTIFY_CONDVAR(cv)       ((void)0)
#define JS_NOTIFY_ALL_CONDVAR(cv)   ((void)0)

#define JS_LOCK_RUNTIME(rt)         ((void)0)
#define JS_UNLOCK_RUNTIME(rt)       ((void)0)
#define JS_LOCK_OBJ(cx,obj)         ((void)0)
#define JS_UNLOCK_OBJ(cx,obj)       ((void)0)
#define JS_LOCK_OBJ_VOID(cx,obj,e)  (e)
#define JS_LOCK_SCOPE(cx,scope)     ((void)0)
#define JS_UNLOCK_SCOPE(cx,scope)   ((void)0)
#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0)

#define JS_IS_RUNTIME_LOCKED(rt)    1
#define JS_IS_OBJ_LOCKED(obj)       1
#define JS_IS_SCOPE_LOCKED(scope)   1
#define JS_LOCK_VOID(cx, e)         JS_LOCK_RUNTIME_VOID((cx)->runtime, e)

#endif /* !JS_THREADSAFE */

#define JS_LOCK_RUNTIME_VOID(rt,e)                                            \
    JS_BEGIN_MACRO                                                            \
	JS_LOCK_RUNTIME(rt);                                                  \
	e;                                                                    \
	JS_UNLOCK_RUNTIME(rt);                                                \
    JS_END_MACRO

#define JS_LOCK_GC(rt)              JS_ACQUIRE_LOCK((rt)->gcLock)
#define JS_UNLOCK_GC(rt)            JS_RELEASE_LOCK((rt)->gcLock)
#define JS_LOCK_GC_VOID(rt,e)       (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt))
#define JS_AWAIT_GC_DONE(rt)        JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)
#define JS_NOTIFY_GC_DONE(rt)       JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)
#define JS_AWAIT_REQUEST_DONE(rt)   JS_WAIT_CONDVAR((rt)->requestDone,        \
						    JS_NO_TIMEOUT)
#define JS_NOTIFY_REQUEST_DONE(rt)  JS_NOTIFY_CONDVAR((rt)->requestDone)

#define JS_LOCK(P,CX) JS_LOCK0(P,(CX)->thread)
#define JS_UNLOCK(P,CX) JS_UNLOCK0(P,(CX)->thread)

#ifndef _SET_OBJ_INFO
#define _SET_OBJ_INFO(obj,f,l)      ((void)0)
#endif
#ifndef _SET_SCOPE_INFO
#define _SET_SCOPE_INFO(scope,f,l)  ((void)0)
#endif

#endif /* jslock_h___ */

**** End of jslock.h. ****

**** Start of jslog2.c. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "jsbit.h"

/*
** Compute the log of the least power of 2 greater than or equal to n
*/
JS_EXPORT_API(JSIntn) JS_CeilingLog2(JSUint32 n)
{
    JSIntn log2 = 0;

    if (n & (n-1))
	log2++;
    if (n >> 16)
	log2 += 16, n >>= 16;
    if (n >> 8)
	log2 += 8, n >>= 8;
    if (n >> 4)
	log2 += 4, n >>= 4;
    if (n >> 2)
	log2 += 2, n >>= 2;
    if (n >> 1)
	log2++;
    return log2;
}

/*
** Compute the log of the greatest power of 2 less than or equal to n.
** This really just finds the highest set bit in the word.
*/
JS_EXPORT_API(JSIntn) JS_FloorLog2(JSUint32 n)
{
    JSIntn log2 = 0;

    if (n >> 16)
	log2 += 16, n >>= 16;
    if (n >> 8)
	log2 += 8, n >>= 8;
    if (n >> 4)
	log2 += 4, n >>= 4;
    if (n >> 2)
	log2 += 2, n >>= 2;
    if (n >> 1)
	log2++;
    return log2;
}

**** End of jslog2.c. ****

**** Start of jslong.c. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "jstypes.h"
#include "jslong.h"

static JSInt64 ll_zero = JSLL_INIT( 0x00000000,0x00000000 );
static JSInt64 ll_maxint = JSLL_INIT( 0x7fffffff, 0xffffffff );
static JSInt64 ll_minint = JSLL_INIT( 0x80000000, 0x00000000 );

#ifdef HAVE_WATCOM_BUG_2
JSInt64 __pascal __loadds __export
    JSLL_Zero(void) { return ll_zero; }
JSInt64 __pascal __loadds __export
    JSLL_MaxInt(void) { return ll_maxint; }
JSInt64 __pascal __loadds __export
    JSLL_MinInt(void) { return ll_minint; }
#else
JS_EXPORT_API(JSInt64) JSLL_Zero(void) { return ll_zero; }
JS_EXPORT_API(JSInt64) JSLL_MaxInt(void) { return ll_maxint; }
JS_EXPORT_API(JSInt64) JSLL_MinInt(void) { return ll_minint; }
#endif

#ifndef JS_HAVE_LONG_LONG
/*
** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1.
*/
static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b)
{
    JSUint32 d1, d0, q1, q0;
    JSUint32 r1, r0, m;

    d1 = jshi16(b);
    d0 = jslo16(b);
    r1 = a.hi % d1;
    q1 = a.hi / d1;
    m = q1 * d0;
    r1 = (r1 << 16) | jshi16(a.lo);
    if (r1 < m) {
        q1--, r1 += b;
        if (r1 >= b	/* i.e., we didn't get a carry when adding to r1 */
	    && r1 < m) {
	    q1--, r1 += b;
	}
    }
    r1 -= m;
    r0 = r1 % d1;
    q0 = r1 / d1;
    m = q0 * d0;
    r0 = (r0 << 16) | jslo16(a.lo);
    if (r0 < m) {
        q0--, r0 += b;
        if (r0 >= b
	    && r0 < m) {
	    q0--, r0 += b;
	}
    }
    *qp = (q1 << 16) | q0;
    *rp = r0 - m;
}

static JSUint32 CountLeadingZeros(JSUint32 a)
{
    JSUint32 t;
    JSUint32 r = 32;

    if ((t = a >> 16) != 0)
	r -= 16, a = t;
    if ((t = a >> 8) != 0)
	r -= 8, a = t;
    if ((t = a >> 4) != 0)
	r -= 4, a = t;
    if ((t = a >> 2) != 0)
	r -= 2, a = t;
    if ((t = a >> 1) != 0)
	r -= 1, a = t;
    if (a & 1)
	r--;
    return r;
}

JS_EXPORT_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b)
{
    JSUint32 n0, n1, n2;
    JSUint32 q0, q1;
    JSUint32 rsh, lsh;

    n0 = a.lo;
    n1 = a.hi;

    if (b.hi == 0) {
	if (b.lo > n1) {
	    /* (0 q0) = (n1 n0) / (0 D0) */

	    lsh = CountLeadingZeros(b.lo);

	    if (lsh) {
		/*
		 * Normalize, i.e. make the most significant bit of the
		 * denominator be set.
		 */
		b.lo = b.lo << lsh;
		n1 = (n1 << lsh) | (n0 >> (32 - lsh));
		n0 = n0 << lsh;
	    }

	    a.lo = n0, a.hi = n1;
	    norm_udivmod32(&q0, &n0, a, b.lo);
	    q1 = 0;

	    /* remainder is in n0 >> lsh */
	} else {
	    /* (q1 q0) = (n1 n0) / (0 d0) */

	    if (b.lo == 0)		/* user wants to divide by zero! */
		b.lo = 1 / b.lo;	/* so go ahead and crash */

	    lsh = CountLeadingZeros(b.lo);

	    if (lsh == 0) {
		/*
		 * From (n1 >= b.lo)
		 *   && (the most significant bit of b.lo is set),
		 * conclude that
		 *	(the most significant bit of n1 is set)
		 *   && (the leading quotient digit q1 = 1).
		 *
		 * This special case is necessary, not an optimization
		 * (Shifts counts of 32 are undefined).
		 */
		n1 -= b.lo;
		q1 = 1;
	    } else {
		/*
		 * Normalize.
		 */
		rsh = 32 - lsh;

		b.lo = b.lo << lsh;
		n2 = n1 >> rsh;
		n1 = (n1 << lsh) | (n0 >> rsh);
		n0 = n0 << lsh;

		a.lo = n1, a.hi = n2;
		norm_udivmod32(&q1, &n1, a, b.lo);
	    }

	    /* n1 != b.lo... */

	    a.lo = n0, a.hi = n1;
	    norm_udivmod32(&q0, &n0, a, b.lo);

	    /* remainder in n0 >> lsh */
	}

	if (rp) {
	    rp->lo = n0 >> lsh;
	    rp->hi = 0;
	}
    } else {
	if (b.hi > n1) {
	    /* (0 0) = (n1 n0) / (D1 d0) */

	    q0 = 0;
	    q1 = 0;

	    /* remainder in (n1 n0) */
	    if (rp) {
		rp->lo = n0;
		rp->hi = n1;
	    }
	} else {
	    /* (0 q0) = (n1 n0) / (d1 d0) */

	    lsh = CountLeadingZeros(b.hi);
	    if (lsh == 0) {
		/*
		 * From (n1 >= b.hi)
		 *   && (the most significant bit of b.hi is set),
		 * conclude that
		 *      (the most significant bit of n1 is set)
		 *   && (the quotient digit q0 = 0 or 1).
		 *
		 * This special case is necessary, not an optimization.
		 */

		/*
		 * The condition on the next line takes advantage of that
		 * n1 >= b.hi (true due to control flow).
		 */
		if (n1 > b.hi || n0 >= b.lo) {
		    q0 = 1;
		    a.lo = n0, a.hi = n1;
		    JSLL_SUB(a, a, b);
		} else {
		    q0 = 0;
		}
		q1 = 0;

		if (rp) {
		    rp->lo = n0;
		    rp->hi = n1;
		}
	    } else {
		JSInt64 m;

		/*
		 * Normalize.
		 */
		rsh = 32 - lsh;

		b.hi = (b.hi << lsh) | (b.lo >> rsh);
		b.lo = b.lo << lsh;
		n2 = n1 >> rsh;
		n1 = (n1 << lsh) | (n0 >> rsh);
		n0 = n0 << lsh;

		a.lo = n1, a.hi = n2;
		norm_udivmod32(&q0, &n1, a, b.hi);
		JSLL_MUL32(m, q0, b.lo);

		if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) {
		    q0--;
		    JSLL_SUB(m, m, b);
		}

		q1 = 0;

		/* Remainder is ((n1 n0) - (m1 m0)) >> lsh */
		if (rp) {
		    a.lo = n0, a.hi = n1;
		    JSLL_SUB(a, a, m);
		    rp->lo = (a.hi << rsh) | (a.lo >> lsh);
		    rp->hi = a.hi >> lsh;
		}
	    }
	}
    }

    if (qp) {
	qp->lo = q0;
	qp->hi = q1;
    }
}
#endif /* !JS_HAVE_LONG_LONG */

**** End of jslong.c. ****

**** Start of jslong.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
** File:                jslong.h
** Description: Portable access to 64 bit numerics
**
** Long-long (64-bit signed integer type) support. Some C compilers
** don't support 64 bit integers yet, so we use these macros to
** support both machines that do and don't.
**/
#ifndef jslong_h___
#define jslong_h___

#ifndef jstypes_h___
	#include "jstypes.h"
#endif

JS_BEGIN_EXTERN_C

/***********************************************************************
** DEFINES:     JSLL_MaxInt
**              JSLL_MinInt
**              JSLL_Zero
** DESCRIPTION:
**      Various interesting constants and static variable
**      initializer
***********************************************************************/
#ifdef HAVE_WATCOM_BUG_2
JSInt64 __pascal __loadds __export
    JSLL_MaxInt(void);
JSInt64 __pascal __loadds __export
    JSLL_MinInt(void);
JSInt64 __pascal __loadds __export
    JSLL_Zero(void);
#else
JS_EXTERN_API(JSInt64) JSLL_MaxInt(void);
JS_EXTERN_API(JSInt64) JSLL_MinInt(void);
JS_EXTERN_API(JSInt64) JSLL_Zero(void);
#endif

#define JSLL_MAXINT   JSLL_MaxInt()
#define JSLL_MININT   JSLL_MinInt()
#define JSLL_ZERO     JSLL_Zero()

#ifdef JS_HAVE_LONG_LONG

#if JS_BYTES_PER_LONG == 8
#define JSLL_INIT(hi, lo)  ((hi ## L << 32) + lo ## L)
#elif defined(WIN32) || defined(WIN16)
#define JSLL_INIT(hi, lo)  ((hi ## i64 << 32) + lo ## i64)
#else
#define JSLL_INIT(hi, lo)  ((hi ## LL << 32) + lo ## LL)
#endif

/***********************************************************************
** MACROS:      JSLL_*
** DESCRIPTION:
**      The following macros define portable access to the 64 bit
**      math facilities.
**
***********************************************************************/

/***********************************************************************
** MACROS:      JSLL_<relational operators>
**
**  JSLL_IS_ZERO        Test for zero
**  JSLL_EQ             Test for equality
**  JSLL_NE             Test for inequality
**  JSLL_GE_ZERO        Test for zero or positive
**  JSLL_CMP            Compare two values
***********************************************************************/
#define JSLL_IS_ZERO(a)       ((a) == 0)
#define JSLL_EQ(a, b)         ((a) == (b))
#define JSLL_NE(a, b)         ((a) != (b))
#define JSLL_GE_ZERO(a)       ((a) >= 0)
#define JSLL_CMP(a, op, b)    ((JSInt64)(a) op (JSInt64)(b))
#define JSLL_UCMP(a, op, b)   ((JSUint64)(a) op (JSUint64)(b))

/***********************************************************************
** MACROS:      JSLL_<logical operators>
**
**  JSLL_AND            Logical and
**  JSLL_OR             Logical or
**  JSLL_XOR            Logical exclusion
**  JSLL_OR2            A disgusting deviation
**  JSLL_NOT            Negation (one's compliment)
***********************************************************************/
#define JSLL_AND(r, a, b)        ((r) = (a) & (b))
#define JSLL_OR(r, a, b)        ((r) = (a) | (b))
#define JSLL_XOR(r, a, b)        ((r) = (a) ^ (b))
#define JSLL_OR2(r, a)        ((r) = (r) | (a))
#define JSLL_NOT(r, a)        ((r) = ~(a))

/***********************************************************************
** MACROS:      JSLL_<mathematical operators>
**
**  JSLL_NEG            Negation (two's compliment)
**  JSLL_ADD            Summation (two's compliment)
**  JSLL_SUB            Difference (two's compliment)
***********************************************************************/
#define JSLL_NEG(r, a)        ((r) = -(a))
#define JSLL_ADD(r, a, b)     ((r) = (a) + (b))
#define JSLL_SUB(r, a, b)     ((r) = (a) - (b))

/***********************************************************************
** MACROS:      JSLL_<mathematical operators>
**
**  JSLL_MUL            Product (two's compliment)
**  JSLL_DIV            Quotient (two's compliment)
**  JSLL_MOD            Modulus (two's compliment)
***********************************************************************/
#define JSLL_MUL(r, a, b)        ((r) = (a) * (b))
#define JSLL_DIV(r, a, b)        ((r) = (a) / (b))
#define JSLL_MOD(r, a, b)        ((r) = (a) % (b))

/***********************************************************************
** MACROS:      JSLL_<shifting operators>
**
**  JSLL_SHL            Shift left [0..64] bits
**  JSLL_SHR            Shift right [0..64] bits with sign extension
**  JSLL_USHR           Unsigned shift right [0..64] bits
**  JSLL_ISHL           Signed shift left [0..64] bits
***********************************************************************/
#define JSLL_SHL(r, a, b)     ((r) = (JSInt64)(a) << (b))
#define JSLL_SHR(r, a, b)     ((r) = (JSInt64)(a) >> (b))
#define JSLL_USHR(r, a, b)    ((r) = (JSUint64)(a) >> (b))
#define JSLL_ISHL(r, a, b)    ((r) = (JSInt64)(a) << (b))

/***********************************************************************
** MACROS:      JSLL_<conversion operators>
**
**  JSLL_L2I            Convert to signed 32 bit
**  JSLL_L2UI           Convert to unsigned 32 bit
**  JSLL_L2F            Convert to floating point
**  JSLL_L2D            Convert to floating point
**  JSLL_I2L            Convert signed to 64 bit
**  JSLL_UI2L           Convert unsigned to 64 bit
**  JSLL_F2L            Convert float to 64 bit
**  JSLL_D2L            Convert float to 64 bit
***********************************************************************/
#define JSLL_L2I(i, l)        ((i) = (JSInt32)(l))
#define JSLL_L2UI(ui, l)        ((ui) = (JSUint32)(l))
#define JSLL_L2F(f, l)        ((f) = (JSFloat64)(l))
#define JSLL_L2D(d, l)        ((d) = (JSFloat64)(l))

#define JSLL_I2L(l, i)        ((l) = (JSInt64)(i))
#define JSLL_UI2L(l, ui)        ((l) = (JSInt64)(ui))
#define JSLL_F2L(l, f)        ((l) = (JSInt64)(f))
#define JSLL_D2L(l, d)        ((l) = (JSInt64)(d))

/***********************************************************************
** MACROS:      JSLL_UDIVMOD
** DESCRIPTION:
**  Produce both a quotient and a remainder given an unsigned 
** INPUTS:      JSUint64 a: The dividend of the operation
**              JSUint64 b: The quotient of the operation
** OUTPUTS:     JSUint64 *qp: pointer to quotient
**              JSUint64 *rp: pointer to remainder
***********************************************************************/
#define JSLL_UDIVMOD(qp, rp, a, b) \
    (*(qp) = ((JSUint64)(a) / (b)), \
     *(rp) = ((JSUint64)(a) % (b)))

#else  /* !JS_HAVE_LONG_LONG */

#ifdef IS_LITTLE_ENDIAN
#define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)}
#else
#define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)}
#endif

#define JSLL_IS_ZERO(a)        (((a).hi == 0) && ((a).lo == 0))
#define JSLL_EQ(a, b)        (((a).hi == (b).hi) && ((a).lo == (b).lo))
#define JSLL_NE(a, b)        (((a).hi != (b).hi) || ((a).lo != (b).lo))
#define JSLL_GE_ZERO(a)        (((a).hi >> 31) == 0)

#define JSLL_CMP(a, op, b)    (((JSInt32)(a).hi op (JSInt32)(b).hi) || \
                 (((a).hi == (b).hi) && ((a).lo op (b).lo)))
#define JSLL_UCMP(a, op, b)    (((a).hi op (b).hi) || \
                 (((a).hi == (b).hi) && ((a).lo op (b).lo)))

#define JSLL_AND(r, a, b)        ((r).lo = (a).lo & (b).lo, \
                 (r).hi = (a).hi & (b).hi)
#define JSLL_OR(r, a, b)        ((r).lo = (a).lo | (b).lo, \
                 (r).hi = (a).hi | (b).hi)
#define JSLL_XOR(r, a, b)        ((r).lo = (a).lo ^ (b).lo, \
                 (r).hi = (a).hi ^ (b).hi)
#define JSLL_OR2(r, a)        ((r).lo = (r).lo | (a).lo, \
                 (r).hi = (r).hi | (a).hi)
#define JSLL_NOT(r, a)        ((r).lo = ~(a).lo, \
                 (r).hi = ~(a).hi)

#define JSLL_NEG(r, a)        ((r).lo = -(JSInt32)(a).lo, \
                 (r).hi = -(JSInt32)(a).hi - ((r).lo != 0))
#define JSLL_ADD(r, a, b) { \
    JSInt64 _a, _b; \
    _a = a; _b = b; \
    (r).lo = _a.lo + _b.lo; \
    (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \
}

#define JSLL_SUB(r, a, b) { \
    JSInt64 _a, _b; \
    _a = a; _b = b; \
    (r).lo = _a.lo - _b.lo; \
    (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \
}

#define JSLL_MUL(r, a, b) { \
    JSInt64 _a, _b; \
    _a = a; _b = b; \
    JSLL_MUL32(r, _a.lo, _b.lo); \
    (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \
}

#define jslo16(a)        ((a) & JS_BITMASK(16))
#define jshi16(a)        ((a) >> 16)

#define JSLL_MUL32(r, a, b) { \
     JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \
     _a1 = jshi16(a), _a0 = jslo16(a); \
     _b1 = jshi16(b), _b0 = jslo16(b); \
     _y0 = _a0 * _b0; \
     _y1 = _a0 * _b1; \
     _y2 = _a1 * _b0; \
     _y3 = _a1 * _b1; \
     _y1 += jshi16(_y0);                         /* can't carry */ \
     _y1 += _y2;                                /* might carry */ \
     if (_y1 < _y2)    \
        _y3 += (JSUint32)(JS_BIT(16));  /* propagate */ \
     (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \
     (r).hi = _y3 + jshi16(_y1); \
}

#define JSLL_UDIVMOD(qp, rp, a, b)    jsll_udivmod(qp, rp, a, b)

JS_EXTERN_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b);

#define JSLL_DIV(r, a, b) { \
    JSInt64 _a, _b; \
    JSUint32 _negative = (JSInt32)(a).hi < 0; \
    if (_negative) { \
    JSLL_NEG(_a, a); \
    } else { \
    _a = a; \
    } \
    if ((JSInt32)(b).hi < 0) { \
    _negative ^= 1; \
    JSLL_NEG(_b, b); \
    } else { \
    _b = b; \
    } \
    JSLL_UDIVMOD(&(r), 0, _a, _b); \
    if (_negative) \
    JSLL_NEG(r, r); \
}

#define JSLL_MOD(r, a, b) { \
    JSInt64 _a, _b; \
    JSUint32 _negative = (JSInt32)(a).hi < 0; \
    if (_negative) { \
    JSLL_NEG(_a, a); \
    } else { \
    _a = a; \
    } \
    if ((JSInt32)(b).hi < 0) { \
    JSLL_NEG(_b, b); \
    } else { \
    _b = b; \
    } \
    JSLL_UDIVMOD(0, &(r), _a, _b); \
    if (_negative) \
    JSLL_NEG(r, r); \
}

#define JSLL_SHL(r, a, b) { \
    if (b) { \
    JSInt64 _a; \
        _a = a; \
        if ((b) < 32) { \
        (r).lo = _a.lo << ((b) & 31); \
        (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \
    } else { \
        (r).lo = 0; \
        (r).hi = _a.lo << ((b) & 31); \
    } \
    } else { \
    (r) = (a); \
    } \
}

/* a is an JSInt32, b is JSInt32, r is JSInt64 */
#define JSLL_ISHL(r, a, b) { \
    if (b) { \
    JSInt64 _a; \
    _a.lo = (a); \
    _a.hi = 0; \
        if ((b) < 32) { \
        (r).lo = (a) << ((b) & 31); \
        (r).hi = ((a) >> (32 - (b))); \
    } else { \
        (r).lo = 0; \
        (r).hi = (a) << ((b) & 31); \
    } \
    } else { \
    (r).lo = (a); \
    (r).hi = 0; \
    } \
}

#define JSLL_SHR(r, a, b) { \
    if (b) { \
    JSInt64 _a; \
        _a = a; \
    if ((b) < 32) { \
        (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \
        (r).hi = (JSInt32)_a.hi >> ((b) & 31); \
    } else { \
        (r).lo = (JSInt32)_a.hi >> ((b) & 31); \
        (r).hi = (JSInt32)_a.hi >> 31; \
    } \
    } else { \
    (r) = (a); \
    } \
}

#define JSLL_USHR(r, a, b) { \
    if (b) { \
    JSInt64 _a; \
        _a = a; \
    if ((b) < 32) { \
        (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \
        (r).hi = _a.hi >> ((b) & 31); \
    } else { \
        (r).lo = _a.hi >> ((b) & 31); \
        (r).hi = 0; \
    } \
    } else { \
    (r) = (a); \
    } \
}

#define JSLL_L2I(i, l)        ((i) = (l).lo)
#define JSLL_L2UI(ui, l)        ((ui) = (l).lo)
#define JSLL_L2F(f, l)        { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; }

#define JSLL_L2D(d, l) { \
    int _negative; \
    JSInt64 _absval; \
 \
    _negative = (l).hi >> 31; \
    if (_negative) { \
    JSLL_NEG(_absval, l); \
    } else { \
    _absval = l; \
    } \
    (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \
    if (_negative) \
    (d) = -(d); \
}

#define JSLL_I2L(l, i)        { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; }
#define JSLL_UI2L(l, ui)      ((l).lo = (ui), (l).hi = 0)
#define JSLL_F2L(l, f)        { double _d = (double)f; JSLL_D2L(l, _d); }

#define JSLL_D2L(l, d) { \
    int _negative; \
    double _absval, _d_hi; \
    JSInt64 _lo_d; \
 \
    _negative = ((d) < 0); \
    _absval = _negative ? -(d) : (d); \
 \
    (l).hi = _absval / 4.294967296e9; \
    (l).lo = 0; \
    JSLL_L2D(_d_hi, l); \
    _absval -= _d_hi; \
    _lo_d.hi = 0; \
    if (_absval < 0) { \
    _lo_d.lo = -_absval; \
    JSLL_SUB(l, l, _lo_d); \
    } else { \
    _lo_d.lo = _absval; \
    JSLL_ADD(l, l, _lo_d); \
    } \
 \
    if (_negative) \
    JSLL_NEG(l, l); \
}

#endif /* !JS_HAVE_LONG_LONG */

JS_END_EXTERN_C

#endif /* jslong_h___ */

**** End of jslong.h. ****

**** Start of jsmath.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS math package.
 */
#include "jsstddef.h"
#include "jslibmath.h"
#include <stdlib.h>
#include "jstypes.h"
#include "jslong.h"
#include "prmjtime.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jslock.h"
#include "jsmath.h"
#include "jsnum.h"
#include "jsobj.h"

#ifndef M_E
#define M_E		2.7182818284590452354
#endif
#ifndef M_LOG2E
#define M_LOG2E		1.4426950408889634074
#endif
#ifndef M_LOG10E
#define M_LOG10E	0.43429448190325182765
#endif
#ifndef M_LN2
#define M_LN2		0.69314718055994530942
#endif
#ifndef M_LN10
#define M_LN10		2.30258509299404568402
#endif
#ifndef M_PI
#define M_PI		3.14159265358979323846
#endif
#ifndef M_SQRT2
#define M_SQRT2		1.41421356237309504880
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2	0.70710678118654752440
#endif

static JSConstDoubleSpec math_constants[] = {
    {M_E,       "E"},
    {M_LOG2E,   "LOG2E"},
    {M_LOG10E,  "LOG10E"},
    {M_LN2,     "LN2"},
    {M_LN10,    "LN10"},
    {M_PI,      "PI"},
    {M_SQRT2,   "SQRT2"},
    {M_SQRT1_2, "SQRT1_2"},
    {0}
};

static JSClass math_class = {
    "Math",
    0,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
};

static JSBool
math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_fabs(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_acos(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_asin(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_atan(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, y, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
	return JS_FALSE;
    if (!js_ValueToNumber(cx, argv[1], &y))
	return JS_FALSE;
    z = fd_atan2(x, y);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_ceil(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_cos(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_exp(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_floor(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_log(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, y, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    if (!js_ValueToNumber(cx, argv[1], &y))
    return JS_FALSE;
    if (JSDOUBLE_IS_NaN(y)||JSDOUBLE_IS_NaN(x))
    {
        *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
        return JS_TRUE;
    }   
    if ((x==0)&&(x==y)&&(fd_copysign(1,y)==-1))
        z = x;
    else
        z = (x > y) ? x : y;
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, y, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
	return JS_FALSE;
    if (!js_ValueToNumber(cx, argv[1], &y))
    return JS_FALSE;
    if (JSDOUBLE_IS_NaN(y)||JSDOUBLE_IS_NaN(x))
    {
        *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
        return JS_TRUE;
    }
    if ((x==0)&&(x==y)&&(fd_copysign(1,y)==-1))
        z = y;
    else
        z = (x < y) ? x : y;
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, y, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
	return JS_FALSE;
    if (!js_ValueToNumber(cx, argv[1], &y))
    return JS_FALSE;
    z = fd_pow(x, y);
    return js_NewNumberValue(cx, z, rval);
}

/*
 * Math.random() support, lifted from java.util.Random.java.
 */
static void
random_setSeed(JSRuntime *rt, int64 seed)
{
    int64 tmp;

    JSLL_I2L(tmp, 1000);
    JSLL_DIV(seed, seed, tmp);
    JSLL_XOR(tmp, seed, rt->rngMultiplier);
    JSLL_AND(rt->rngSeed, tmp, rt->rngMask);
}

static void
random_init(JSRuntime *rt)
{
    int64 tmp, tmp2;

    /* Do at most once. */
    if (rt->rngInitialized)
	return;
    rt->rngInitialized = JS_TRUE;

    /* rt->rngMultiplier = 0x5DEECE66DL */
    JSLL_ISHL(tmp, 0x5D, 32);
    JSLL_UI2L(tmp2, 0xEECE66DL);
    JSLL_OR(rt->rngMultiplier, tmp, tmp2);

    /* rt->rngAddend = 0xBL */
    JSLL_I2L(rt->rngAddend, 0xBL);

    /* rt->rngMask = (1L << 48) - 1 */
    JSLL_I2L(tmp, 1);
    JSLL_SHL(tmp2, tmp, 48);
    JSLL_SUB(rt->rngMask, tmp2, tmp);

    /* rt->rngDscale = (jsdouble)(1L << 54) */
    JSLL_SHL(tmp2, tmp, 54);
    JSLL_L2D(rt->rngDscale, tmp2);

    /* Finally, set the seed from current time. */
    random_setSeed(rt, PRMJ_Now());
}

static uint32
random_next(JSRuntime *rt, int bits)
{
    int64 nextseed, tmp;
    uint32 retval;

    JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
    JSLL_ADD(nextseed, nextseed, rt->rngAddend);
    JSLL_AND(nextseed, nextseed, rt->rngMask);
    rt->rngSeed = nextseed;
    JSLL_USHR(tmp, nextseed, 48 - bits);
    JSLL_L2I(retval, tmp);
    return retval;
}

static jsdouble
random_nextDouble(JSRuntime *rt)
{
    int64 tmp, tmp2;
    jsdouble d;

    JSLL_ISHL(tmp, random_next(rt, 27), 27);
    JSLL_UI2L(tmp2, random_next(rt, 27));
    JSLL_ADD(tmp, tmp, tmp2);
    JSLL_L2D(d, tmp);
    return d / rt->rngDscale;
}

static JSBool
math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSRuntime *rt;
    jsdouble z;

    rt = cx->runtime;
    JS_LOCK_RUNTIME(rt);
    random_init(rt);
    z = random_nextDouble(rt);
    JS_UNLOCK_RUNTIME(rt);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_copysign(fd_floor(x + 0.5), x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_sin(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_sqrt(x);
    return js_NewNumberValue(cx, z, rval);
}

static JSBool
math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x, z;

    if (!js_ValueToNumber(cx, argv[0], &x))
    return JS_FALSE;
    z = fd_tan(x);
    return js_NewNumberValue(cx, z, rval);
}

#if JS_HAS_TOSOURCE
static JSBool
math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    *rval = ATOM_KEY(cx->runtime->atomState.MathAtom);
    return JS_TRUE;
}
#endif

static JSFunctionSpec math_static_methods[] = {
#ifdef JS_HAS_TOSOURCE
    {js_toSource_str,   math_toSource,  0},
#endif
    {"abs",		math_abs,		1},
    {"acos",		math_acos,		1},
    {"asin",		math_asin,		1},
    {"atan",		math_atan,		1},
    {"atan2",		math_atan2,		2},
    {"ceil",		math_ceil,		1},
    {"cos",		math_cos,		1},
    {"exp",		math_exp,		1},
    {"floor",		math_floor,		1},
    {"log",		math_log,		1},
    {"max",		math_max,		2},
    {"min",		math_min,		2},
    {"pow",		math_pow,		2},
    {"random",		math_random,		0},
    {"round",		math_round,		1},
    {"sin",		math_sin,		1},
    {"sqrt",		math_sqrt,		1},
    {"tan",		math_tan,		1},
    {0}
};

JSObject *
js_InitMathClass(JSContext *cx, JSObject *obj)
{
    JSObject *Math;
    
    Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0);
    if (!Math)
        return NULL;
    if (!JS_DefineFunctions(cx, Math, math_static_methods))
        return NULL;
    if (!JS_DefineConstDoubles(cx, Math, math_constants))
        return NULL;
    return Math;
}

**** End of jsmath.c. ****

**** Start of jsmath.h. ****

/* -*- Mode: C; tab-width: 8 -*-
 * Copyright (C) 1998 Netscape Communications Corporation, All Rights Reserved.
 */

#ifndef jsmath_h___
#define jsmath_h___
/*
 * JS math functions.
 */

JS_BEGIN_EXTERN_C

extern JSObject *
js_InitMathClass(JSContext *cx, JSObject *obj);

JS_END_EXTERN_C

#endif /* jsmath_h___ */

**** End of jsmath.h. ****

**** Start of jsnum.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS number type and wrapper class.
 */
#include "jsstddef.h"
#include <errno.h>
#ifdef XP_PC
#include <float.h>
#endif
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsdtoa.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsstr.h"

union dpun {
    struct {
#ifdef IS_LITTLE_ENDIAN
	uint32 lo, hi;
#else
	uint32 hi, lo;
#endif
    } s;
    jsdouble d;
};

static JSBool
num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x;

    if (!js_ValueToNumber(cx, argv[0], &x))
	return JS_FALSE;
    *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x));
    return JS_TRUE;
}

static JSBool
num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble x;

    if (!js_ValueToNumber(cx, argv[0], &x))
	return JS_FALSE;
    *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x));
    return JS_TRUE;
}

static JSBool
num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    jsdouble d;
    const jschar *ep;

    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;
    if (!js_strtod(cx, str->chars, &ep, &d))
	return JS_FALSE;
    if (ep == str->chars) {
	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
	return JS_TRUE;
    }
    return js_NewNumberValue(cx, d, rval);
}

/* See ECMA 15.1.2.2. */
static JSBool
num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    jsint radix;
    jsdouble d;
    const jschar *ep;

    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;

    if (argc > 1) {
	if (!js_ValueToECMAInt32(cx, argv[1], &radix))
	    return JS_FALSE;
    } else
	radix = 0;

    if (radix != 0 && (radix < 2 || radix > 36)) {
	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
	return JS_TRUE;
    }
    if (!js_strtointeger(cx, str->chars, &ep, radix, &d))
	return JS_FALSE;
    if (ep == str->chars) {
	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
	return JS_TRUE;
    }
    return js_NewNumberValue(cx, d, rval);
}


static JSFunctionSpec number_functions[] = {
    {"isNaN",           num_isNaN,              1},
    {"isFinite",        num_isFinite,           1},
    {"parseFloat",      num_parseFloat,         1},
    {"parseInt",        num_parseInt,           2},
    {0}
};

static JSClass number_class = {
    "Number",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
};

static JSBool
Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsdouble d;
    jsval v;

    if (argc != 0) {
	if (!js_ValueToNumber(cx, argv[0], &d))
	    return JS_FALSE;
    } else {
	d = 0.0;
    }
    if (!js_NewNumberValue(cx, d, &v))
	return JS_FALSE;
    if (!cx->fp->constructing) {
	*rval = v;
	return JS_TRUE;
    }
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
    return JS_TRUE;
}

#if JS_HAS_TOSOURCE
static JSBool
num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval v;
    jsdouble d;
    size_t i;
    char buf[64];
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &number_class, argv))
	return JS_FALSE;
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_NUMBER(v))
	return js_obj_toSource(cx, obj, argc, argv, rval);
    d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
    i = JS_snprintf(buf, sizeof buf, "(new %s(", number_class.name);

    JS_cnvtf(buf + i, sizeof buf - i, 20, d);
    i = strlen(buf);
    JS_snprintf(buf + i, sizeof buf - i, "))");
    str = JS_NewStringCopyZ(cx, buf);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#endif

static JSBool
num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval v;
    jsdouble d;
    jsint base, ival, dval;
    char *bp, buf[32];
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &number_class, argv))
	return JS_FALSE;
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_NUMBER(v))
	return js_obj_toString(cx, obj, argc, argv, rval);
    d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
    if (argc != 0) {
	if (!js_ValueToECMAInt32(cx, argv[0], &base))
	    return JS_FALSE;
	if (base < 2 || base > 36) {
	    char numBuf[12];
	    JS_snprintf(numBuf, sizeof numBuf, "%ld", (long) base);
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX,
	    			 numBuf);
	    return JS_FALSE;
	}
	if (base != 10 && JSDOUBLE_IS_FINITE(d)) {
	    ival = (jsint) js_DoubleToInteger(d);
	    bp = buf + sizeof buf;
	    for (*--bp = '\0'; ival != 0 && --bp >= buf; ival /= base) {
		dval = ival % base;
		*bp = (char)((dval >= 10) ? 'a' - 10 + dval : '0' + dval);
	    }
	    if (*bp == '\0')
		*--bp = '0';
	    str = JS_NewStringCopyZ(cx, bp);
	} else {
	    str = js_NumberToString(cx, d);
	}
    } else {
	str = js_NumberToString(cx, d);
    }
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (!JS_InstanceOf(cx, obj, &number_class, argv))
	return JS_FALSE;
    *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    return JS_TRUE;
}

static JSFunctionSpec number_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   num_toSource,   0},
#endif
    {js_toString_str,	num_toString,	0},
    {js_valueOf_str,	num_valueOf,	0},
    {0}
};

/* NB: Keep this in synch with number_constants[]. */
enum nc_slot {
    NC_NaN,
    NC_POSITIVE_INFINITY,
    NC_NEGATIVE_INFINITY,
    NC_MAX_VALUE,
    NC_MIN_VALUE,
    NC_LIMIT
};

/*
 * Some to most C compilers forbid spelling these at compile time, or barf
 * if you try, so all but MAX_VALUE are set at runtime by js_InitNumberClass
 * using union dpun.
 */
static JSConstDoubleSpec number_constants[] = {
    {0,                         "NaN"},
    {0,                         "POSITIVE_INFINITY"},
    {0,                         "NEGATIVE_INFINITY"},
    {1.7976931348623157E+308,   "MAX_VALUE"},
    {0,                         "MIN_VALUE"},
    {0}
};

static jsdouble NaN;

JSObject *
js_InitNumberClass(JSContext *cx, JSObject *obj)
{
    JSRuntime *rt;
    union dpun u;
    JSObject *proto, *ctor;

    rt = cx->runtime;
    if (!rt->jsNaN) {
#ifdef XP_PC
#ifdef XP_OS2
	/*DSR071597 - I have no idea what this really does other than mucking with the floating     */
	/*point unit, but it does fix a "floating point underflow" exception I am getting, and there*/
	/*is similar code in the Hursley java. Making sure we have the same code in Javascript      */
	/*where Netscape was calling control87 on Windows...                                        */
	_control87(MCW_EM+PC_53+RC_NEAR,MCW_EM+MCW_PC+MCW_RC);
#else
	_control87(MCW_EM, MCW_EM);
#endif
#endif

	u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;
	u.s.lo = 0xffffffff;
	number_constants[NC_NaN].dval = NaN = u.d;
	rt->jsNaN = js_NewDouble(cx, NaN);
	if (!rt->jsNaN || !js_LockGCThing(cx, rt->jsNaN))
	    return NULL;

	u.s.hi = JSDOUBLE_HI32_EXPMASK;
	u.s.lo = 0x00000000;
	number_constants[NC_POSITIVE_INFINITY].dval = u.d;
	rt->jsPositiveInfinity = js_NewDouble(cx, u.d);
	if (!rt->jsPositiveInfinity ||
	    !js_LockGCThing(cx, rt->jsPositiveInfinity)) {
	    return NULL;
	}

	u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
	u.s.lo = 0x00000000;
	number_constants[NC_NEGATIVE_INFINITY].dval = u.d;
	rt->jsNegativeInfinity = js_NewDouble(cx, u.d);
	if (!rt->jsNegativeInfinity ||
	    !js_LockGCThing(cx, rt->jsNegativeInfinity)) {
	    return NULL;
	}

	u.s.hi = 0;
	u.s.lo = 1;
	number_constants[NC_MIN_VALUE].dval = u.d;
    }

    if (!JS_DefineFunctions(cx, obj, number_functions))
	return NULL;

    proto = JS_InitClass(cx, obj, NULL, &number_class, Number, 1,
			 NULL, number_methods, NULL, NULL);
    if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
	return NULL;
    OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO);
    if (!JS_DefineConstDoubles(cx, ctor, number_constants))
	return NULL;

    /* ECMA 15.1.1.1 */
    if (!JS_DefineProperty(cx, obj, "NaN", DOUBLE_TO_JSVAL(rt->jsNaN),
			   NULL, NULL, 0)) {
	return NULL;
    }

    /* ECMA 15.1.1.2 */
    if (!JS_DefineProperty(cx, obj, "Infinity",
			   DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),
			   NULL, NULL, 0)) {
	return NULL;
    }
    return proto;
}

jsdouble *
js_NewDouble(JSContext *cx, jsdouble d)
{
    jsdouble *dp;

    dp = js_AllocGCThing(cx, GCX_DOUBLE);
    if (!dp)
	return NULL;
    *dp = d;
    return dp;
}

void
js_FinalizeDouble(JSContext *cx, jsdouble *dp)
{
    *dp = NaN;
}

JSBool
js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
{
    jsdouble *dp;

    dp = js_NewDouble(cx, d);
    if (!dp)
	return JS_FALSE;
    *rval = DOUBLE_TO_JSVAL(dp);
    return JS_TRUE;
}

JSBool
js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
{
    jsint i;

    if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
	*rval = INT_TO_JSVAL(i);
    } else {
	if (!js_NewDoubleValue(cx, d, rval))
	    return JS_FALSE;
    }
    return JS_TRUE;
}

JSObject *
js_NumberToObject(JSContext *cx, jsdouble d)
{
    JSObject *obj;
    jsval v;

    obj = js_NewObject(cx, &number_class, NULL, NULL);
    if (!obj)
	return NULL;
    if (!js_NewNumberValue(cx, d, &v)) {
	cx->newborn[GCX_OBJECT] = NULL;
	return NULL;
    }
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
    return obj;
}

/* XXXbe rewrite me to be ECMA-based! */
JSString *
js_NumberToString(JSContext *cx, jsdouble d)
{
    jsint i;
    char buf[32];

    if (JSDOUBLE_IS_INT(d, i)) {
	JS_snprintf(buf, sizeof buf, "%ld", (long)i);
    } else {
	JS_cnvtf(buf, sizeof buf, 20, d);
    }
    return JS_NewStringCopyZ(cx, buf);
}

JSBool
js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
{
    JSObject *obj;
    JSString *str;
    const jschar *ep;
    jsdouble d;

    if (JSVAL_IS_OBJECT(v)) {
	obj = JSVAL_TO_OBJECT(v);
	if (!obj) {
	    *dp = 0;
	    return JS_TRUE;
	}
	if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v))
	    return JS_FALSE;
    }
    if (JSVAL_IS_INT(v)) {
	*dp = (jsdouble)JSVAL_TO_INT(v);
    } else if (JSVAL_IS_DOUBLE(v)) {
	*dp = *JSVAL_TO_DOUBLE(v);
    } else if (JSVAL_IS_STRING(v)) {
	str = JSVAL_TO_STRING(v);
	errno = 0;
	/* Note that ECMAScript doesn't treat numbers beginning with a zero as octal numbers here.
	 * This works because all such numbers will be interpreted as decimal by js_strtod and
	 * will never get passed to js_strtointeger, which would interpret them as octal. */
	if ((!js_strtod(cx, str->chars, &ep, &d) || js_SkipWhiteSpace(ep) != str->chars + str->length) &&
	    (!js_strtointeger(cx, str->chars, &ep, 0, &d) || js_SkipWhiteSpace(ep) != str->chars + str->length)) {
	    goto badstr;
	}
	*dp = d;
    } else if (JSVAL_IS_BOOLEAN(v)) {
	*dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0;
    } else {
#if JS_BUG_FALLIBLE_TONUM
	str = js_DecompileValueGenerator(cx, v, NULL);
badstr:
	if (str) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NAN,
				 JS_GetStringBytes(str));

	}
	return JS_FALSE;
#else
badstr:
	*dp = *cx->runtime->jsNaN;
#endif
    }
    return JS_TRUE;
}

JSBool
js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
{
    jsdouble d;

    if (!js_ValueToNumber(cx, v, &d))
	return JS_FALSE;
    return js_DoubleToECMAInt32(cx, d, ip);
}

JSBool
js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip)
{
    jsdouble two32 = 4294967296.0;
    jsdouble two31 = 2147483648.0;

    if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
	*ip = 0;
	return JS_TRUE;
    }
    d = fmod(d, two32);
    d = d >= 0 ? d : d + two32;
    if (d >= two31)
	*ip = (int32)(d - two32);
    else
	*ip = (int32)d;
    return JS_TRUE;
}

JSBool
js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
{
    jsdouble d;

    if (!js_ValueToNumber(cx, v, &d))
	return JS_FALSE;
    return js_DoubleToECMAUint32(cx, d, ip);
}

JSBool
js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip)
{
    JSBool neg;
    jsdouble two32 = 4294967296.0;

    if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
	*ip = 0;
	return JS_TRUE;
    }

    neg = (d < 0);
    d = floor(neg ? -d : d);
    d = neg ? -d : d;

    d = fmod(d, two32);

    d = d >= 0 ? d : d + two32;
    *ip = (uint32)d;
    return JS_TRUE;
}

JSBool
js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
{
    jsdouble d;
    JSString *str;

    if (!js_ValueToNumber(cx, v, &d))
	return JS_FALSE;
    if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
	str = js_DecompileValueGenerator(cx, v, NULL);
	if (str) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_CANT_CONVERT, JS_GetStringBytes(str));

	}
	return JS_FALSE;
    }
    *ip = (int32)floor(d + 0.5);     /* Round to nearest */
    return JS_TRUE;
}

JSBool
js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
{
    jsdouble d;
    jsuint i, m;
    JSBool neg;

    if (!js_ValueToNumber(cx, v, &d))
	return JS_FALSE;
    if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
	*ip = 0;
	return JS_TRUE;
    }
    i = (jsuint)d;
    if ((jsdouble)i == d) {
	*ip = (uint16)i;
	return JS_TRUE;
    }
    neg = (d < 0);
    d = floor(neg ? -d : d);
    d = neg ? -d : d;
    m = JS_BIT(16);
    d = fmod(d, m);
    if (d < 0)
	d += m;
    *ip = (uint16) d;
    return JS_TRUE;
}

jsdouble
js_DoubleToInteger(jsdouble d)
{
    JSBool neg;

    if (d == 0)
	return d;
    if (!JSDOUBLE_IS_FINITE(d)) {
	if (JSDOUBLE_IS_NaN(d))
	    return 0;
	return d;
    }
    neg = (d < 0);
    d = floor(neg ? -d : d);
    return neg ? -d : d;
}


JSBool
js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
{
    size_t i;
    char *cstr, *istr, *estr;
    JSBool negative;
    jsdouble d;
    const jschar *s1 = js_SkipWhiteSpace(s);
    size_t length = js_strlen(s1);

    cstr = malloc(length + 1);
    if (!cstr)
	return JS_FALSE;
    for (i = 0; i <= length; i++) {
	if (s1[i] >> 8) {
	    cstr[i] = 0;
	    break;
	}
	cstr[i] = (char)s1[i];
    }

    istr = cstr;
    if ((negative = (*istr == '-')) != 0 || *istr == '+')
	istr++;
    if (!strncmp(istr, "Infinity", 8)) {
	d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity);
	estr = istr + 8;
    } else {
	errno = 0;
	d = JS_strtod(cstr, &estr);
	if (errno == ERANGE)
	    if (d == HUGE_VAL)
		d = *cx->runtime->jsPositiveInfinity;
	    else if (d == -HUGE_VAL)
		d = *cx->runtime->jsNegativeInfinity;
#ifdef HPUX
        if (d == 0.0 && negative) {
            /* 
             * "-0", "-1e-2000" come out as positive zero
    		 * here on HPUX. Force a negative zero instead.
             */
            JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT;
            JSDOUBLE_LO32(d) = 0;
        }
#endif
    }

    free(cstr);
    i = estr - cstr;
    *ep = i ? s1 + i : s;
    *dp = d;
    return JS_TRUE;
}

struct BinaryDigitReader
{
    uintN base;			/* Base of number; must be a power of 2 */
    uintN digit;		/* Current digit value in radix given by base */
    uintN digitMask;		/* Mask to extract the next bit from digit */
    const jschar *digits;	/* Pointer to the remaining digits */
    const jschar *end;		/* Pointer to first non-digit */
};

/* Return the next binary digit from the number or -1 if done */
static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)
{
    intN bit;

    if (bdr->digitMask == 0) {
	uintN c;

	if (bdr->digits == bdr->end)
	    return -1;

	c = *bdr->digits++;
	if ('0' <= c && c <= '9')
	    bdr->digit = c - '0';
	else if ('a' <= c && c <= 'z')
	    bdr->digit = c - 'a' + 10;
	else bdr->digit = c - 'A' + 10;
	bdr->digitMask = bdr->base >> 1;
    }
    bit = (bdr->digit & bdr->digitMask) != 0;
    bdr->digitMask >>= 1;
    return bit;
}

JSBool
js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
{
    JSBool negative;
    jsdouble value;
    const jschar *start;
    const jschar *s1 = js_SkipWhiteSpace(s);

    if ((negative = (*s1 == '-')) != 0 || *s1 == '+')
	s1++;

    if (base == 0)
	/* No base supplied, or some base that evaluated to 0. */
	if (*s1 == '0')
	    /* It's either hex or octal; only increment char if str isn't '0' */
	    if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */
		s1 += 2;
		base = 16;
	    } else /* Octal */
		base = 8;
	else
	    base = 10; /* Default to decimal. */
    else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x'))
	/* If base is 16, ignore hex prefix. */
	s1 += 2;

    /* Done with the preliminaries; find some prefix of the string that's
     * a number in the given base.
     */
    start = s1; /* Mark - if string is empty, we return NaN. */
    value = 0.0;
    while (1) {
	uintN digit;
	jschar c = *s1;
	if ('0' <= c && c <= '9')
	    digit = c - '0';
	else if ('a' <= c && c <= 'z')
	    digit = c - 'a' + 10;
	else if ('A' <= c && c <= 'Z')
	    digit = c - 'A' + 10;
	else
	    break;
	if (digit >= (uintN)base)
	    break;
	value = value*base + digit;
	s1++;
    }

    if (value >= 9007199254740992.0)
	if (base == 10) {
	    /* If we're accumulating a decimal number and the number is >= 2^53, then
	     * the result from the repeated multiply-add above may be inaccurate.  Call
	     * JS_strtod to get the correct answer.
	     */
	    size_t i;
	    size_t length = s1 - start;
	    char *cstr = malloc(length + 1);
	    char *estr;

	    if (!cstr)
		return JS_FALSE;
	    for (i = 0; i != length; i++)
		cstr[i] = (char)start[i];
	    cstr[length] = 0;

	    errno = 0;
	    value = JS_strtod(cstr, &estr);
	    if (errno == ERANGE && value == HUGE_VAL)
		value = *cx->runtime->jsPositiveInfinity;
	    free(cstr);

	} else if (base == 2 || base == 4 || base == 8 || base == 16 || base == 32) {
	    /* The number may also be inaccurate for one of these bases.  This
	     * happens if the addition in value*base + digit causes a round-down
	     * to an even least significant mantissa bit when the first dropped bit
	     * is a one.  If any of the following digits in the number (which haven't
	     * been added in yet) are nonzero then the correct action would have
	     * been to round up instead of down.  An example of this occurs when
	     * reading the number 0x1000000000000081, which rounds to 0x1000000000000000
	     * instead of 0x1000000000000100.
	     */
	    struct BinaryDigitReader bdr;
	    intN bit, bit2;
	    intN j;

	    bdr.base = base;
	    bdr.digitMask = 0;
	    bdr.digits = start;
	    bdr.end = s1;
	    value = 0.0;

	    /* Skip leading zeros. */
	    do {
		bit = GetNextBinaryDigit(&bdr);
	    } while (bit == 0);

	    if (bit == 1) {
		/* Gather the 53 significant bits (including the leading 1) */
		value = 1.0;
		for (j = 52; j; j--) {
		    bit = GetNextBinaryDigit(&bdr);
		    if (bit < 0)
			goto done;
		    value = value*2 + bit;
		}
		/* bit2 is the 54th bit (the first dropped from the mantissa) */
		bit2 = GetNextBinaryDigit(&bdr);
		if (bit2 >= 0) {
		    jsdouble factor = 2.0;
		    intN sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */
		    intN bit3;

		    while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) {
			sticky |= bit3;
			factor *= 2;
		    }
		    value += bit2 & (bit | sticky);
		    value *= factor;
		}
	      done:;
	    }
	}
    /* We don't worry about inaccurate numbers for any other base. */

    if (s1 == start) {
	*dp = 0.0;
	*ep = s;
    } else {
	*dp = negative ? -value : value;
	*ep = s1;
    }
    return JS_TRUE;
}

**** End of jsnum.c. ****

**** Start of jsnum.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsnum_h___
#define jsnum_h___
/*
 * JS number (IEEE double) interface.
 *
 * JS numbers are optimistically stored in the top 31 bits of 32-bit integers,
 * but floating point literals, results that overflow 31 bits, and division and
 * modulus operands and results require a 64-bit IEEE double.  These are GC'ed
 * and pointed to by 32-bit jsvals on the stack and in object properties.
 *
 * When a JS number is treated as an object (followed by . or []), the runtime
 * wraps it with a JSObject whose valueOf method returns the unwrapped number.
 */

JS_BEGIN_EXTERN_C

#ifdef IS_LITTLE_ENDIAN
#define JSDOUBLE_HI32(x)        (((uint32 *)&(x))[1])
#define JSDOUBLE_LO32(x)        (((uint32 *)&(x))[0])
#else
#define JSDOUBLE_HI32(x)        (((uint32 *)&(x))[0])
#define JSDOUBLE_LO32(x)        (((uint32 *)&(x))[1])
#endif
#define JSDOUBLE_HI32_SIGNBIT   0x80000000
#define JSDOUBLE_HI32_EXPMASK   0x7ff00000
#define JSDOUBLE_HI32_MANTMASK  0x000fffff

#define JSDOUBLE_IS_NaN(x)                                                    \
    ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK &&   \
     (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))

#define JSDOUBLE_IS_INFINITE(x)                                               \
    ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK &&   \
     !JSDOUBLE_LO32(x))

#define JSDOUBLE_IS_FINITE(x)                                                 \
    ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK)

#define JSDOUBLE_IS_NEGZERO(d)  (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \
				 JSDOUBLE_LO32(d) == 0)

/*
 * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid
 * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is
 * safe) leaves i as (jsint)d.  This also avoid anomalous NaN floating point
 * comparisons under MSVC.
 */
#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d) \
			       && ((d) == (i = (jsint)(d))))

/* Initialize the Number class, returning its prototype object. */
extern JSObject *
js_InitNumberClass(JSContext *cx, JSObject *obj);

/* GC-allocate a new JS number. */
extern jsdouble *
js_NewDouble(JSContext *cx, jsdouble d);

extern void
js_FinalizeDouble(JSContext *cx, jsdouble *dp);

extern JSBool
js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval);

extern JSBool
js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);

/* Construct a Number instance that wraps around d. */
extern JSObject *
js_NumberToObject(JSContext *cx, jsdouble d);

/* Convert a number to a GC'ed string. */
extern JSString *
js_NumberToString(JSContext *cx, jsdouble d);

/*
 * Convert a value to a number, returning false after reporting any error,
 * otherwise returning true with *dp set.
 */
extern JSBool
js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp);

/*
 * Convert a value or a double to an int32, according to the ECMA rules
 * for ToInt32.
 */
extern JSBool
js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip);

extern JSBool
js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip);

/*
 * Convert a value or a double to a uint32, according to the ECMA rules
 * for ToUint32.
 */
extern JSBool
js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip);

extern JSBool
js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip);

/*
 * Convert a value to a number, then to an int32 if it fits by rounding to
 * nearest; but failing with an error report if the double is out of range
 * or unordered.
 */
extern JSBool
js_ValueToInt32(JSContext *cx, jsval v, int32 *ip);

/*
 * Convert a value to a number, then to a uint16 according to the ECMA rules
 * for ToUint16.
 */
extern JSBool
js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip);

/*
 * Convert a jsdouble to an integral number, stored in a jsdouble.
 * If d is NaN, return 0.  If d is an infinity, return it without conversion.
 */
extern jsdouble
js_DoubleToInteger(jsdouble d);

/*
 * Similar to strtod except that replaces overflows with infinities of the correct
 * sign and underflows with zeros of the correct sign.  Guaranteed to return the
 * closest double number to the given input in dp.
 * Also allows inputs of the form [+|-]Infinity, which produce an infinity of the
 * appropriate sign.  The case of the "Infinity" string must match.
 * If the string does not have a number in it, set *ep to s and return 0.0 in dp.
 * Return false if out of memory.
 */
extern JSBool
js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp);

/*
 * Similar to strtol except that handles integers of arbitrary size.  Guaranteed to
 * return the closest double number to the given input when radix is 10 or a power of 2.
 * May experience roundoff errors for very large numbers of a different radix.
 * If the string does not have a number in it, set *ep to s and return 0.0 in dp.
 * Return false if out of memory.
 */
extern JSBool
js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp);

JS_END_EXTERN_C

#endif /* jsnum_h___ */

**** End of jsnum.h. ****

**** Start of jsobj.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS object implementation.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jshash.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

#if JS_HAS_OBJ_WATCHPOINT
#include "jsdbgapi.h"
#endif

#ifdef JS_THREADSAFE
#define NATIVE_DROP_PROPERTY js_DropProperty

extern void
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
#else
#define NATIVE_DROP_PROPERTY NULL
#endif

#ifdef XP_MAC
#pragma export on
#endif

JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
    js_NewObjectMap,    js_DestroyObjectMap,
#if defined JS_THREADSAFE && defined DEBUG
    _js_LookupProperty, js_DefineProperty,
#else
    js_LookupProperty,  js_DefineProperty,
#endif
    js_GetProperty,     js_SetProperty,
    js_GetAttributes,   js_SetAttributes,
    js_DeleteProperty,  js_DefaultValue,
    js_Enumerate,       js_CheckAccess,
    NULL,               NATIVE_DROP_PROPERTY,
    js_Call,            js_Construct,
    NULL,               js_HasInstance
};

#ifdef XP_MAC
#pragma export off
#endif

JSClass js_ObjectClass = {
    js_Object_str,
    0,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
};

#if JS_HAS_OBJ_PROTO_PROP

static JSBool
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

JS_STATIC_DLL_CALLBACK(JSBool)
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

static JSBool
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

static JSPropertySpec object_props[] = {
    /* These two must come first; see object_props[slot].name usage below. */
    {js_proto_str, JSSLOT_PROTO,  JSPROP_PERMANENT, obj_getSlot,  obj_setSlot},
    {js_parent_str,JSSLOT_PARENT, JSPROP_PERMANENT, obj_getSlot,  obj_setSlot},
    {js_count_str, 0,             JSPROP_PERMANENT, obj_getCount, obj_getCount},
    {0}
};

static JSBool
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint slot;
    JSAccessMode mode;
    uintN attrs;

    slot = JSVAL_TO_INT(id);
    if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
	id = (jsid)cx->runtime->atomState.protoAtom;
	mode = JSACC_PROTO;
    } else {
	id = (jsid)cx->runtime->atomState.parentAtom;
	mode = JSACC_PARENT;
    }
    if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
	return JS_FALSE;
    *vp = OBJ_GET_SLOT(cx, obj, slot);
    return JS_TRUE;
}

JS_STATIC_DLL_CALLBACK(JSBool)
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSObject *obj2;
    jsint slot;

    if (!JSVAL_IS_OBJECT(*vp))
	return JS_TRUE;
    obj2 = JSVAL_TO_OBJECT(*vp);
    slot = JSVAL_TO_INT(id);
    while (obj2) {
	if (obj2 == obj) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_CYCLIC_VALUE, object_props[slot].name);
	    return JS_FALSE;
	}
	obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
    }
    OBJ_SET_SLOT(cx, obj, slot, *vp);
    return JS_TRUE;
}

static JSBool
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsval iter_state;
    jsid num_properties;

    iter_state = JSVAL_NULL;

    /* Get the number of properties to enumerate. */
    if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties))
	goto error;
    if (!JSVAL_IS_INT(num_properties)) {
	JS_ASSERT(0);
	goto error;
    }
    *vp = num_properties;
    return JS_TRUE;

error:
    if (iter_state != JSVAL_NULL)
	OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
    return JS_FALSE;
}

#else  /* !JS_HAS_OBJ_PROTO_PROP */

#define object_props NULL

#endif /* !JS_HAS_OBJ_PROTO_PROP */

JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_object(const void *key)
{
    return (JSHashNumber)key >> JSVAL_TAGBITS;
}

static JSHashEntry *
MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
{
    JSSharpObjectMap *map;
    JSHashTable *table;
    JSHashNumber hash;
    JSHashEntry **hep, *he;
    jsatomid sharpid;
    JSIdArray *ida;
    JSBool ok;
    jsint i, length;
    jsval val;

    map = &cx->sharpObjectMap;
    table = map->table;
    hash = js_hash_object(obj);
    hep = JS_HashTableRawLookup(table, hash, obj);
    he = *hep;
    if (!he) {
	sharpid = 0;
	he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
	if (!he) {
	    JS_ReportOutOfMemory(cx);
	    return NULL;
	}
	ida = JS_Enumerate(cx, obj);
	if (!ida)
	    return NULL;
	ok = JS_TRUE;
	for (i = 0, length = ida->length; i < length; i++) {
	    ok = OBJ_GET_PROPERTY(cx, obj, ida->vector[i], &val);
	    if (!ok)
		break;
	    if (!JSVAL_IS_PRIMITIVE(val) &&
		!MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
		ok = JS_FALSE;
		break;
	    }
	}
	if (!ok || !idap)
	    JS_DestroyIdArray(cx, ida);
	if (!ok)
	    return NULL;
    } else {
	sharpid = (jsatomid) he->value;
	if (sharpid == 0) {
	    sharpid = ++map->sharpgen << 1;
	    he->value = (void *) sharpid;
	}
	ida = NULL;
    }
    if (idap)
	*idap = ida;
    return he;
}

JSHashEntry *
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
		    jschar **sp)
{
    JSSharpObjectMap *map;
    JSHashTable *table;
    JSIdArray *ida;
    JSHashNumber hash;
    JSHashEntry *he, **hep;
    jsatomid sharpid;
    char buf[20];
    size_t len;

    map = &cx->sharpObjectMap;
    table = map->table;
    if (!table) {
	table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
				JS_CompareValues, NULL, NULL);
	if (!table) {
	    JS_ReportOutOfMemory(cx);
	    return NULL;
	}
	map->table = table;
    }

    ida = NULL;
    if (map->depth == 0) {
	he = MarkSharpObjects(cx, obj, &ida);
	if (!he)
	    return NULL;
	JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
	if (!idap) {
	    JS_DestroyIdArray(cx, ida);
	    ida = NULL;
	}
    } else {
	hash = js_hash_object(obj);
	hep = JS_HashTableRawLookup(table, hash, obj);
	he = *hep;

	/*
	 * It's possible that the value of a property has changed from the
	 * first time the object's properties are traversed (when the property
	 * ids are entered into the hash table) to the second (when they are
	 * converted to strings), i.e. the getProperty() call is not
	 * idempotent.
	 */
	if (!he) {
	    he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)0);
	    if (!he)
		JS_ReportOutOfMemory(cx);
	    *sp = NULL;
            sharpid = 0;
	    goto out;
	}
    }

    sharpid = (jsatomid) he->value;
    if (sharpid == 0) {
	*sp = NULL;
    } else {
	len = JS_snprintf(buf, sizeof buf, "#%u%c",
			  sharpid >> 1, (sharpid & SHARP_BIT) ? '#' : '=');
	*sp = js_InflateString(cx, buf, len);
	if (!*sp) {
	    if (ida)
		JS_DestroyIdArray(cx, ida);
	    return NULL;
	}
    }

out:
    if ((sharpid & SHARP_BIT) == 0) {
	if (idap && !ida) {
	    ida = JS_Enumerate(cx, obj);
	    if (!ida) {
		if (*sp) {
		    JS_free(cx, *sp);
		    *sp = NULL;
		}
		return NULL;
	    }
	}
	map->depth++;
    }

    if (idap)
	*idap = ida;
    return he;
}

void
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
{
    JSSharpObjectMap *map;
    JSIdArray *ida;

    map = &cx->sharpObjectMap;
    JS_ASSERT(map->depth > 0);
    if (--map->depth == 0) {
	map->sharpgen = 0;
	JS_HashTableDestroy(map->table);
	map->table = NULL;
    }
    if (idap) {
	ida = *idap;
	if (ida) {
	    JS_DestroyIdArray(cx, ida);
	    *idap = NULL;
	}
    }
}

#define OBJ_TOSTRING_EXTRA	2	/* for 2 GC roots */

#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
JSBool
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    JSHashEntry *he;
    JSIdArray *ida;
    jschar *chars, *ochars, *vchars, *vsharp;
    size_t nchars, vlength, vsharplength;
    JSBool ok;
    char *comma;
    jsint i, length;
    jsid id;
    jsval val;
    JSString *idstr, *valstr, *str;

    he = js_EnterSharpObject(cx, obj, &ida, &chars);
    if (!he)
	return JS_FALSE;
    if (IS_SHARP(he)) {	/* we didn't enter -- obj is already sharp */
	JS_ASSERT(!ida);
#if JS_HAS_SHARP_VARS
	nchars = js_strlen(chars);
#else
	chars[0] = '{';
	chars[1] = '}';
	chars[2] = 0;
	nchars = 2;
#endif
	goto make_string;
    }
    JS_ASSERT(ida);
    ok = JS_TRUE;

    /* Allocate 2 + 1 for "{}" and the terminator. */
    if (!chars) {
	chars = malloc((2 + 1) * sizeof(jschar));
	nchars = 0;
	if (!chars)
	    goto error;
    } else {
	MAKE_SHARP(he);
	nchars = js_strlen(chars);
	chars = realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
	if (!chars) {
	    free(ochars);
	    goto error;
	}
    }
    chars[nchars++] = '{';

    comma = NULL;

    for (i = 0, length = ida->length; i < length; i++) {
	/* Get strings for id and val and GC-root them via argv. */
	id = ida->vector[i];
	ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
	if (!ok)
	    goto error;

	/* Convert id to a jsval and then to a string. */
	id = js_IdToValue(id);
	idstr = js_ValueToString(cx, id);
	if (!idstr) {
	    ok = JS_FALSE;
	    goto error;
	}
	argv[0] = STRING_TO_JSVAL(idstr);

	/* If id is a non-identifier string, it needs to be quoted. */
	if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) {
	    idstr = js_QuoteString(cx, idstr, '\'');
	    if (!idstr) {
		ok = JS_FALSE;
		goto error;
	    }
	    argv[0] = STRING_TO_JSVAL(idstr);
	}

	/* Convert val to its canonical source form. */
	valstr = js_ValueToSource(cx, val);
	if (!valstr) {
	    ok = JS_FALSE;
	    goto error;
	}
	argv[1] = STRING_TO_JSVAL(valstr);
	vchars = valstr->chars;
	vlength = valstr->length;

	/* If val is a non-sharp object, consider sharpening it. */
	vsharp = NULL;
	vsharplength = 0;
#if JS_HAS_SHARP_VARS
	if (!JSVAL_IS_PRIMITIVE(val) && vchars[0] != '#') {
	    he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val), NULL, &vsharp);
	    if (!he) {
		ok = JS_FALSE;
		goto error;
	    }
	    if (IS_SHARP(he)) {
		vchars = vsharp;
		vlength = js_strlen(vchars);
	    } else {
		if (vsharp) {
		    vsharplength = js_strlen(vsharp);
		    MAKE_SHARP(he);
		}
		js_LeaveSharpObject(cx, NULL);
	    }
	}
#endif

	/* Allocate 1 + 1 at end for closing brace and terminating 0. */
	chars = realloc((ochars = chars),
			(nchars + (comma ? 2 : 0) +
			 idstr->length + 1 + vsharplength + vlength +
			 1 + 1) * sizeof(jschar));
	if (!chars) {
	    /* Save code space on error: let JS_free ignore null vsharp. */
	    JS_free(cx, vsharp);
	    free(ochars);
	    goto error;
	}

	if (comma) {
	    chars[nchars++] = comma[0];
	    chars[nchars++] = comma[1];
	}
	comma = ", ";

	js_strncpy(&chars[nchars], idstr->chars, idstr->length);
	nchars += idstr->length;
	chars[nchars++] = ':';

	if (vsharplength) {
	    js_strncpy(&chars[nchars], vsharp, vsharplength);
	    nchars += vsharplength;
	}
	js_strncpy(&chars[nchars], vchars, vlength);
	nchars += vlength;

	if (vsharp)
	    JS_free(cx, vsharp);
    }

    if (chars) {
	chars[nchars++] = '}';
	chars[nchars] = 0;
    }

error:
    js_LeaveSharpObject(cx, &ida);

    if (!ok) {
	if (chars)
	    free(chars);
	return ok;
    }

    if (!chars) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
  make_string:
    str = js_NewString(cx, chars, nchars, 0);
    if (!str) {
	free(chars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */

JSBool
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    jschar *chars;
    size_t nchars;
    char *clazz, *prefix;
    JSString *str;

#if JS_HAS_INITIALIZERS
    if (cx->version == JSVERSION_1_2)
	return js_obj_toSource(cx, obj, argc, argv, rval);
#endif

    clazz = OBJ_GET_CLASS(cx, obj)->name;
    nchars = 9 + strlen(clazz);		/* 9 for "[object ]" */
    chars = JS_malloc(cx, (nchars + 1) * sizeof(jschar));
    if (!chars)
	return JS_FALSE;

    prefix = "[object ";
    nchars = 0;
    while ((chars[nchars] = (jschar)*prefix) != 0)
	nchars++, prefix++;
    while ((chars[nchars] = (jschar)*clazz) != 0)
	nchars++, clazz++;
    chars[nchars++] = ']';
    chars[nchars] = 0;

    str = js_NewString(cx, chars, nchars, 0);
    if (!str) {
	JS_free(cx, chars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    *rval = OBJECT_TO_JSVAL(obj);
    return JS_TRUE;
}

static JSBool
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSStackFrame *caller;
    JSObject *scopeobj;
    JSString *str;
    const char *file;
    uintN line;
    JSPrincipals *principals;
    JSScript *script;
    JSBool ok;
#if JS_HAS_EVAL_THIS_SCOPE
    JSObject *callerScopeChain;
    JSBool implicitWith;
#endif

    caller = cx->fp->down;
    implicitWith = JS_FALSE; /* Unnecessary init to kill gcc warning */

    if ((cx->version == JSVERSION_DEFAULT || cx->version >= JSVERSION_1_4)
            && (*caller->pc != JSOP_CALLSPECIAL)) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_BAD_INDIRECT_CALL, js_eval_str);
        return JS_FALSE;
    }

    if (!JSVAL_IS_STRING(argv[0])) {
	*rval = argv[0];
	return JS_TRUE;
    }

#if JS_HAS_SCRIPT_OBJECT
    /*
     * Script.prototype.compile/exec and Object.prototype.eval all take an
     * optional trailing argument that overrides the scope object.
     */
    scopeobj = NULL;
    if (argc >= 2) {
	if (!js_ValueToObject(cx, argv[1], &scopeobj))
	    return JS_FALSE;
	argv[1] = OBJECT_TO_JSVAL(scopeobj);
    }
    if (!scopeobj)
#endif
    {
#if JS_HAS_EVAL_THIS_SCOPE
	/* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
	callerScopeChain = caller->scopeChain;
	implicitWith = (callerScopeChain != obj &&
			(OBJ_GET_CLASS(cx, callerScopeChain) != &js_WithClass ||
			 OBJ_GET_PROTO(cx, callerScopeChain) != obj));
	if (implicitWith) {
	    scopeobj = js_NewObject(cx, &js_WithClass, obj, callerScopeChain);
	    if (!scopeobj)
		return JS_FALSE;
	    caller->scopeChain = scopeobj;
	}
	/* From here on, control must exit through label out with ok set. */
#endif

#if JS_BUG_EVAL_THIS_SCOPE
	/* An old version used the object in which eval was found for scope. */
	scopeobj = obj;
#else
	/* Compile using caller's current scope object (might be a function). */
	scopeobj = caller->scopeChain;
#endif
    }

    str = JSVAL_TO_STRING(argv[0]);
    if (caller->script) {
	file = caller->script->filename;
	line = js_PCToLineNumber(caller->script, caller->pc);
	principals = caller->script->principals;
    } else {
	file = NULL;
	line = 0;
	principals = NULL;
    }
    script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
					     str->chars, str->length,
					     file, line);
    if (!script) {
	ok = JS_FALSE;
	goto out;
    }

#if JS_HAS_SCRIPT_OBJECT
    if (argc < 2)
#endif
#if !JS_BUG_EVAL_THIS_SCOPE
    {
	/* Execute using caller's new scope object (might be a Call object). */
	scopeobj = caller->scopeChain;
    }
#endif
    ok = js_Execute(cx, scopeobj, script, caller->fun, caller, JS_FALSE, rval);
    JS_DestroyScript(cx, script);

out:
#if JS_HAS_EVAL_THIS_SCOPE
    /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
    if (implicitWith)
	caller->scopeChain = OBJ_GET_PARENT(cx, scopeobj);
#endif
    return ok;
}

#if JS_HAS_OBJ_WATCHPOINT

static JSBool
obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
		  void *closure)
{
    JSObject *funobj;
    jsval argv[3];

    funobj = closure;
    argv[0] = id;
    argv[1] = old;
    argv[2] = *nvp;
    return js_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
}

static JSBool
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSFunction *fun;
    jsval userid, value;
    jsid symid;
    JSAtom *atom;
    uintN attrs;

    fun = js_ValueToFunction(cx, &argv[1], JS_FALSE);
    if (!fun)
	return JS_FALSE;
    argv[1] = OBJECT_TO_JSVAL(fun->object);

    /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
    userid = argv[0];
    if (JSVAL_IS_INT(userid)) {
	symid = (jsid)userid;
	atom = NULL;
    } else {
	atom = js_ValueToStringAtom(cx, userid);
	if (!atom)
	    return JS_FALSE;
	symid = (jsid)atom;
    }

    if (!OBJ_CHECK_ACCESS(cx, obj, symid, JSACC_WATCH, &value, &attrs))
	return JS_FALSE;
    if (attrs & JSPROP_READONLY)
	return JS_TRUE;
    return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);
}

static JSBool
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
    return JS_TRUE;
}

#endif /* JS_HAS_OBJ_WATCHPOINT */

#if JS_HAS_NEW_OBJ_METHODS
/*
 * Prototype and property query methods, to complement the 'in' and
 * 'instanceof' operators.
 */

/* Proposed ECMA 15.2.4.5. */
static JSBool
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                   jsval *rval)
{
    JSObject *obj2;
    JSProperty *prop;
    JSAtom *atom;
    
    atom = js_ValueToStringAtom(cx, *argv);
    if (atom == NULL || !OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
        return JS_FALSE;
    *rval = (!prop || obj2 != obj) ? JSVAL_FALSE : JSVAL_TRUE;
    if (prop)
        OBJ_DROP_PROPERTY(cx, obj2, prop);
    return JS_TRUE;
}

/* Proposed ECMA 15.2.4.6. */
static JSBool
obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                  jsval *rval)
{
    JSBool b;

    if (!js_IsDelegate(cx, obj, *argv, &b))
        return JS_FALSE;
    *rval = b ? JSVAL_TRUE : JSVAL_FALSE;
    return JS_TRUE;
}

/* Proposed ECMA 15.2.4.7. */
static JSBool
obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                         jsval *rval)
{
    JSObject *obj2;
    JSProperty *prop;
    JSAtom *atom;
    uintN attrs;
    JSBool ok;
    
    atom = js_ValueToStringAtom(cx, *argv);
    if (atom == NULL || !OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))
        return JS_FALSE;
    if (!prop) {
        *rval = JSVAL_FALSE;
        return JS_TRUE;
    }
    ok = OBJ_GET_ATTRIBUTES(cx, obj2, (jsid)atom, prop, &attrs);
    if (ok)
        *rval = (attrs & JSPROP_ENUMERATE) != 0 ? JSVAL_TRUE : JSVAL_FALSE;
    OBJ_DROP_PROPERTY(cx, obj2, prop);
    return ok;
}
#endif /* JS_HAS_NEW_OBJ_METHODS */

static JSFunctionSpec object_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   js_obj_toSource,        0, 0, OBJ_TOSTRING_EXTRA},
#endif
    {js_toString_str,	js_obj_toString,	0, 0, OBJ_TOSTRING_EXTRA},
    {js_valueOf_str,	obj_valueOf,		0},
    {js_eval_str,	obj_eval,		1, 0, 1},
#if JS_HAS_OBJ_WATCHPOINT
    {"watch",           obj_watch,              2},
    {"unwatch",         obj_unwatch,            1},
#endif
#if JS_HAS_NEW_OBJ_METHODS
    {"hasOwnProperty",  obj_hasOwnProperty,     1},
    {"isPrototypeOf",   obj_isPrototypeOf,      1},
    {"propertyIsEnumerable", obj_propertyIsEnumerable, 1},
#endif
    {0}
};

static JSBool
Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (argc == 0) {
	/* Trigger logic below to construct a blank object. */
	obj = NULL;
    } else {
	/* If argv[0] is null or undefined, obj comes back null. */
	if (!js_ValueToObject(cx, argv[0], &obj))
	    return JS_FALSE;
    }
    if (!obj) {
	JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
	if (cx->fp->constructing)
	    return JS_TRUE;
	obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
    }
    *rval = OBJECT_TO_JSVAL(obj);
    return JS_TRUE;
}

/*
 * ObjectOps and Class for with-statement stack objects.
 */
static JSBool
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
		    JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
		    , const char *file, uintN line
#endif
		    )
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_LookupProperty(cx, obj, id, objp, propp);
    return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
}

static JSBool
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_GetProperty(cx, obj, id, vp);
    return OBJ_GET_PROPERTY(cx, proto, id, vp);
}

static JSBool
with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_SetProperty(cx, obj, id, vp);
    return OBJ_SET_PROPERTY(cx, proto, id, vp);
}

static JSBool
with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
		   uintN *attrsp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_GetAttributes(cx, obj, id, prop, attrsp);
    return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
}

static JSBool
with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
		   uintN *attrsp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_SetAttributes(cx, obj, id, prop, attrsp);
    return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
}

static JSBool
with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_DeleteProperty(cx, obj, id, rval);
    return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
}

static JSBool
with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_DefaultValue(cx, obj, hint, vp);
    return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
}

static JSBool
with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
	       jsval *statep, jsid *idp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_Enumerate(cx, obj, enum_op, statep, idp);
    return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
}

static JSBool
with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
		 jsval *vp, uintN *attrsp)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
    return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
}

static JSObject *
with_ThisObject(JSContext *cx, JSObject *obj)
{
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
    if (!proto)
	return obj;
    return OBJ_THIS_OBJECT(cx, proto);
}

JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
    js_NewObjectMap,        js_DestroyObjectMap,
    with_LookupProperty,    js_DefineProperty,
    with_GetProperty,       with_SetProperty,
    with_GetAttributes,     with_SetAttributes,
    with_DeleteProperty,    with_DefaultValue,
    with_Enumerate,         with_CheckAccess,
    with_ThisObject,        NATIVE_DROP_PROPERTY
};

static JSObjectOps *
with_getObjectOps(JSContext *cx, JSClass *clasp)
{
    return &js_WithObjectOps;
}

JSClass js_WithClass = {
    "With",
    0,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
    with_getObjectOps
};

#if JS_HAS_OBJ_PROTO_PROP
static JSBool
With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSObject *parent, *proto;
    jsval v;

    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(obj);
    }

    parent = cx->fp->scopeChain;
    if (argc > 0) {
	if (!js_ValueToObject(cx, argv[0], &proto))
	    return JS_FALSE;
	v = OBJECT_TO_JSVAL(proto);
	if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
	    return JS_FALSE;
	if (argc > 1) {
	    if (!js_ValueToObject(cx, argv[1], &parent))
		return JS_FALSE;
	}
    }
    v = OBJECT_TO_JSVAL(parent);
    return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
}
#endif

JSObject *
js_InitObjectClass(JSContext *cx, JSObject *obj)
{
    JSObject *proto;

#if JS_HAS_SHARP_VARS
    JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
#endif

    proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
			 object_props, object_methods, NULL, NULL);
#if JS_HAS_OBJ_PROTO_PROP
    if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
		      NULL, NULL, NULL, NULL)) {
	return NULL;
    }
#endif
    return proto;
}

void
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
		 JSClass *clasp)
{
    map->nrefs = nrefs;
    map->ops = ops;
    map->nslots = 0;
    map->freeslot = JSSLOT_FREE(clasp);
}

JSObjectMap *
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
		JSClass *clasp, JSObject *obj)
{
    return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
}

void
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
{
    js_DestroyScope(cx, (JSScope *)map);
}

JSObjectMap *
js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
{
    JS_ASSERT(map->nrefs >= 0);
    JS_ATOMIC_ADDREF(&map->nrefs, 1);
    return map;
}

JSObjectMap *
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
{
    JS_ASSERT(map->nrefs > 0);
    JS_ATOMIC_ADDREF(&map->nrefs, -1);
    if (map->nrefs == 0) {
	map->ops->destroyObjectMap(cx, map);
	return NULL;
    }
    if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
	((JSScope *)map)->object = NULL;
    return map;
}

JSObject *
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
{
    JSObject *obj, *ctor;
    JSObjectOps *ops;
    JSObjectMap *map;
    jsval cval;
    uint32 i;

    /* Allocate an object from the GC heap and zero it. */
    obj = js_AllocGCThing(cx, GCX_OBJECT);
    if (!obj)
	return NULL;

    /* Bootstrap the ur-object, and make it the default prototype object. */
    if (!proto) {
	if (!js_GetClassPrototype(cx, clasp->name, &proto))
	    goto bad;
	if (!proto && !js_GetClassPrototype(cx, js_ObjectClass.name, &proto))
	    goto bad;
    }

    /* Always call the class's getObjectOps hook if it has one. */
    ops = clasp->getObjectOps
	  ? clasp->getObjectOps(cx, clasp)
	  : &js_ObjectOps;

    if (proto && (map = proto->map)->ops == ops) {
	/* Default parent to the parent of the prototype's constructor. */
	if (!parent) {
	    if (!OBJ_GET_PROPERTY(cx, proto,
				  (jsid)cx->runtime->atomState.constructorAtom,
				  &cval)) {
		goto bad;
	    }
	    if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
		parent = OBJ_GET_PARENT(cx, ctor);
	}

	/* Share the given prototype's map. */
	obj->map = js_HoldObjectMap(cx, map);
    } else {
	/* Leave parent alone.  Allocate a new map for obj. */
	map = ops->newObjectMap(cx, 1, ops, clasp, obj);
	if (!map)
	    goto bad;
	if (map->nslots == 0)
	    map->nslots = JS_INITIAL_NSLOTS;
	obj->map = map;
    }

    /* Set the proto, parent, and class properties. */
    obj->slots = JS_malloc(cx, JS_INITIAL_NSLOTS * sizeof(jsval));
    if (!obj->slots)
	goto bad;
    obj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
    obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
    obj->slots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
    for (i = JSSLOT_CLASS+1; i < JS_INITIAL_NSLOTS; i++)
	obj->slots[i] = JSVAL_VOID;

    if (cx->runtime->objectHook) {
        cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
    }

    return obj;

bad:
    cx->newborn[GCX_OBJECT] = NULL;
    return NULL;
}

static JSBool
FindConstructor(JSContext *cx, const char *name, jsval *vp)
{
    JSAtom *atom;
    JSObject *obj, *tmp;
    JSObject *pobj;
    JSScopeProperty *sprop;

    atom = js_Atomize(cx, name, strlen(name), 0);
    if (!atom)
	return JS_FALSE;

    if (cx->fp && (tmp = cx->fp->scopeChain) != NULL) {
	/* Find the topmost object in the scope chain. */
	do {
	    obj = tmp;
	    tmp = OBJ_GET_PARENT(cx, obj);
	} while (tmp);
    } else {
	obj = cx->globalObject;
	if (!obj) {
	    *vp = JSVAL_VOID;
	    return JS_TRUE;
	}
    }

    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
	return JS_FALSE;
    if (!sprop)  {
	*vp = JSVAL_VOID;
	return JS_TRUE;
    }

    JS_ASSERT(OBJ_IS_NATIVE(pobj));
    *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
    return JS_TRUE;
}

JSObject *
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
		   JSObject *parent)
{
    JSObject *obj, *ctor;
    JSBool ok;
    jsval cval, *sp, *oldsp, rval;
    void *mark;
    JSStackFrame *fp;

    ok = FindConstructor(cx, clasp->name, &cval);
    if (!ok)
	return NULL;

    /*
     * If proto or parent are NULL, set them to Constructor.prototype and/or
     * Constructor.__parent__, just like JSOP_NEW does.
     */
    ctor = JSVAL_TO_OBJECT(cval);
    if (!parent)
	parent = OBJ_GET_PARENT(cx, ctor);
    if (!proto) {
	if (!OBJ_GET_PROPERTY(cx, ctor,
			      (jsid)cx->runtime->atomState.classPrototypeAtom,
			      &rval)) {
	    return NULL;
	}
	if (JSVAL_IS_OBJECT(rval))
	    proto = JSVAL_TO_OBJECT(rval);
    }

    obj = js_NewObject(cx, clasp, proto, parent);
    if (!obj)
	return NULL;

    /* Allocate stack space for cval and obj, then push them. */
    sp = js_AllocStack(cx, 2, &mark);
    if (!sp)
	goto bad;
    *sp++ = cval;
    *sp++ = OBJECT_TO_JSVAL(obj);

    /* Lift current frame to include the args and do the call. */
    fp = cx->fp;
    oldsp = fp->sp;
    fp->sp = sp;
    ok = js_Invoke(cx, 0, JS_TRUE);
    rval = sp[-1];
    fp->sp = oldsp;
    js_FreeStack(cx, mark);

    if (!ok)
	goto bad;
    return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
bad:
    cx->newborn[GCX_OBJECT] = NULL;
    return NULL;
}

void
js_FinalizeObject(JSContext *cx, JSObject *obj)
{
    JSObjectMap *map;

    /* Cope with stillborn objects that have no map. */
    map = obj->map;
    if (!map)
	return;

    if (cx->runtime->objectHook) {
        cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
    }

#if JS_HAS_OBJ_WATCHPOINT
    /* Remove all watchpoints with weak links to obj. */
    JS_ClearWatchPointsForObject(cx, obj);
#endif

    /* Finalize obj first, in case it needs map and slots. */
    OBJ_GET_CLASS(cx, obj)->finalize(cx, obj);

    /* Drop map and free slots. */
    js_DropObjectMap(cx, map, obj);
    obj->map = NULL;
    JS_free(cx, obj->slots);
    obj->slots = NULL;
}

JSBool
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
{
    JSObjectMap *map;
    uint32 nslots;
    size_t nbytes;
    jsval *newslots;

    map = obj->map;
    nslots = map->nslots;
    if (map->freeslot >= nslots) {
	nslots = JS_MAX(map->freeslot, nslots);
	if (nslots < JS_INITIAL_NSLOTS)
	    nslots = JS_INITIAL_NSLOTS;
	else
	    nslots += (nslots + 1) / 2;

	nbytes = (size_t)nslots * sizeof(jsval);
#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800
	if (nbytes > 60000U) {
	    JS_ReportOutOfMemory(cx);
	    return JS_FALSE;
	}
#endif

	if (obj->slots) {
	    newslots = JS_realloc(cx, obj->slots, nbytes);
	} else {
	    /* obj must be newborn and unshared at this point. */
	    newslots = JS_malloc(cx, nbytes);
	}
	if (!newslots)
	    return JS_FALSE;
	obj->slots = newslots;
	map->nslots = nslots;
    }

#ifdef TOO_MUCH_GC
    obj->slots[map->freeslot] = JSVAL_VOID;
#endif
    *slotp = map->freeslot++;
    return JS_TRUE;
}

void
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
{
    JSObjectMap *map;
    uint32 nslots;
    size_t nbytes;
    jsval *newslots;

    obj->slots[slot] = JSVAL_VOID;
    map = obj->map;
    if (map->freeslot == slot + 1)
	map->freeslot = slot;
    nslots = map->nslots;
    if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
	nslots = map->freeslot;
	nslots += nslots / 2;
	nbytes = (size_t)nslots * sizeof(jsval);
	newslots = JS_realloc(cx, obj->slots, nbytes);
	if (!newslots)
	    return;
	obj->slots = newslots;
	map->nslots = nslots;
    }
}

#if JS_BUG_EMPTY_INDEX_ZERO
#define CHECK_FOR_EMPTY_INDEX(id)                                             \
    JS_BEGIN_MACRO                                                            \
	if (_str->length == 0)                                                \
	    id = JSVAL_ZERO;                                                  \
    JS_END_MACRO
#else
#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
#endif

/* JSVAL_INT_MAX as a string */
#define JSVAL_INT_MAX_STRING "1073741823"

#define CHECK_FOR_FUNNY_INDEX(id)                                             \
    JS_BEGIN_MACRO                                                            \
	if (!JSVAL_IS_INT(id)) {                                              \
	    JSAtom *_atom = (JSAtom *)id;                                     \
	    JSString *_str = ATOM_TO_STRING(_atom);                           \
	    const jschar *_cp = _str->chars;                                  \
	    if (JS7_ISDEC(*_cp) &&                                            \
		_str->length <= sizeof(JSVAL_INT_MAX_STRING)-1)               \
	    {                                                                 \
		jsuint _index = JS7_UNDEC(*_cp++);                            \
		jsuint _oldIndex = 0;                                         \
		jsuint _c = 0;                                                \
		if (_index != 0) {                                            \
		    while (JS7_ISDEC(*_cp)) {                                 \
			_oldIndex = _index;                                   \
			_c = JS7_UNDEC(*_cp);                                 \
			_index = 10*_index + _c;                              \
			_cp++;                                                \
		    }                                                         \
		}                                                             \
		if (*_cp == 0 &&                                              \
		     (_oldIndex < (JSVAL_INT_MAX / 10) ||                     \
		      (_oldIndex == (JSVAL_INT_MAX / 10) &&                   \
		       _c < (JSVAL_INT_MAX % 10))))                           \
		    id = INT_TO_JSVAL(_index);                                \
	    } else {                                                          \
		CHECK_FOR_EMPTY_INDEX(id);                                    \
	    }                                                                 \
	}                                                                     \
    JS_END_MACRO


JSBool
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
		  JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
		  JSProperty **propp)
{
    JSClass *clasp;
    JSScope *scope;
    JSScopeProperty *sprop;

    /* Handle old bug that treated empty string as zero index.
     * Also convert string indices to numbers if applicable. */
    CHECK_FOR_FUNNY_INDEX(id);

    /* Lock if object locking is required by this implementation. */
    JS_LOCK_OBJ(cx, obj);

    /* Use the object's class getter and setter by default. */
    clasp = LOCKED_OBJ_GET_CLASS(obj);
    if (!getter)
	getter = clasp->getProperty;
    if (!setter)
	setter = clasp->setProperty;

    /* Find a sharable scope, or get a new one for obj. */
    scope = js_MutateScope(cx, obj, id, getter, setter, attrs, &sprop);
    if (!scope)
	goto bad;

    /* Add the property only if MutateScope didn't find a shared scope. */
    if (!sprop) {
	sprop = js_NewScopeProperty(cx, scope, id, getter, setter, attrs);
	if (!sprop)
	    goto bad;
	/* XXXbe called with lock held */
	if (!clasp->addProperty(cx, obj, sprop->id, &value) ||
	    !scope->ops->add(cx, scope, id, sprop)) {
	    js_DestroyScopeProperty(cx, scope, sprop);
	    goto bad;
	}
	PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj, id,
			    (JSProperty *)sprop);
    }

    LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
    if (propp) {
#ifdef JS_THREADSAFE
	js_HoldScopeProperty(cx, scope, sprop);
#endif
	*propp = (JSProperty *) sprop;
    } else {
	JS_UNLOCK_OBJ(cx, obj);
    }
    return JS_TRUE;

bad:
    JS_UNLOCK_OBJ(cx, obj);
    return JS_FALSE;
}

#if defined JS_THREADSAFE && defined DEBUG
JS_FRIEND_API(JSBool)
_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
		   JSProperty **propp, const char *file, uintN line)
#else
JS_FRIEND_API(JSBool)
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
		  JSProperty **propp)
#endif
{
    JSHashNumber hash;
    JSScope *prevscope, *scope;
    JSSymbol *sym;
    JSClass *clasp;
    JSResolveOp resolve;
    JSNewResolveOp newresolve;
    uintN flags;
    uint32 format;
    JSObject *obj2, *proto;
    JSScopeProperty *sprop;

    /* Handle old bug that treated empty string as zero index.
     * Also convert string indices to numbers if applicable. */
    CHECK_FOR_FUNNY_INDEX(id);

    /* Search scopes starting with obj and following the prototype link. */
    hash = js_HashValue(id);
    prevscope = NULL;
    for (;;) {
	JS_LOCK_OBJ(cx, obj);
	_SET_OBJ_INFO(obj, file, line);
	scope = (JSScope *)obj->map;
	if (scope == prevscope)
	    goto skip;
	sym = scope->ops->lookup(cx, scope, id, hash);
	if (!sym) {
	    clasp = LOCKED_OBJ_GET_CLASS(obj);
	    resolve = clasp->resolve;
	    if (resolve != JS_ResolveStub) {
		if (clasp->flags & JSCLASS_NEW_RESOLVE) {
		    newresolve = (JSNewResolveOp)resolve;
		    flags = 0;
		    if (cx->fp && cx->fp->pc) {
			format = js_CodeSpec[*cx->fp->pc].format;
			if ((format & JOF_MODEMASK) != JOF_NAME)
			    flags |= JSRESOLVE_QUALIFIED;
			if (format & JOF_SET)
			    flags |= JSRESOLVE_ASSIGNING;
		    }
		    obj2 = NULL;
		    JS_UNLOCK_OBJ(cx, obj);
		    if (!newresolve(cx, obj, js_IdToValue(id), flags, &obj2))
			return JS_FALSE;
		    JS_LOCK_OBJ(cx, obj);
		    _SET_OBJ_INFO(obj, file, line);
		    if (obj2) {
			scope = (JSScope *)obj2->map;
			if (MAP_IS_NATIVE(&scope->map))
			    sym = scope->ops->lookup(cx, scope, id, hash);
		    }
		} else {
		    JS_UNLOCK_OBJ(cx, obj);
		    if (!resolve(cx, obj, js_IdToValue(id)))
			return JS_FALSE;
		    JS_LOCK_OBJ(cx, obj);
		    _SET_OBJ_INFO(obj, file, line);
		    scope = (JSScope *)obj->map;
		    if (MAP_IS_NATIVE(&scope->map))
			sym = scope->ops->lookup(cx, scope, id, hash);
		}
	    }
	}
	if (sym && (sprop = sym_property(sym)) != NULL) {
	    JS_ASSERT((JSScope *)obj->map == scope);
	    *objp = scope->object;	/* XXXbe hide in jsscope.[ch] */
#ifdef JS_THREADSAFE
	    js_HoldScopeProperty(cx, scope, sprop);
#endif
	    *propp = (JSProperty *) sprop;
	    return JS_TRUE;
	}
	prevscope = scope;
      skip:
	proto = LOCKED_OBJ_GET_PROTO(obj);
	JS_UNLOCK_OBJ(cx, obj);
	if (!proto)
	    break;
	if (!OBJ_IS_NATIVE(proto))
	    return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
	obj = proto;
    }
    *objp = NULL;
    *propp = NULL;
    return JS_TRUE;
}

JS_FRIEND_API(JSBool)
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
		JSProperty **propp)
{
    JSRuntime *rt;
    JSObject *obj, *pobj, *parent, *lastobj;
    JSProperty *prop;

    rt = cx->runtime;

    lastobj = NULL;             /* Suppress gcc warning */
    for (obj = cx->fp->scopeChain; obj; obj = parent) {
	/* Try the property cache and return immediately on cache hit. */
	PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);
	if (PROP_FOUND(prop)) {
#ifdef JS_THREADSAFE
	    JS_ASSERT(OBJ_IS_NATIVE(obj));
	    JS_LOCK_OBJ(cx, obj);
	    JS_ATOMIC_ADDREF(&((JSScopeProperty *)prop)->nrefs, 1);
#endif
	    *objp = obj;
	    *pobjp = obj;
	    *propp = prop;
	    return JS_TRUE;
	}

	/* If cache miss (not cached-as-not-found), take the slow path. */
	if (!prop) {
	    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
		return JS_FALSE;
	    if (prop) {
		PROPERTY_CACHE_FILL(cx, &rt->propertyCache, pobj, id, prop);
		*objp = obj;
		*pobjp = pobj;
		*propp = prop;
		return JS_TRUE;
	    }

	    /* No such property -- cache obj[id] as not-found. */
	    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
				PROP_NOT_FOUND(obj, id));
	}
	parent = OBJ_GET_PARENT(cx, obj);
	lastobj = obj;
    }
    *objp = lastobj;
    *pobjp = NULL;
    *propp = NULL;
    return JS_TRUE;
}

JSBool
js_FindVariable(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
		JSProperty **propp)
{
    JSObject *obj;
    JSProperty *prop;

    /*
     * First look for id's property along the "with" statement and the
     * statically-linked scope chains.
     */
    if (!js_FindProperty(cx, id, objp, pobjp, propp))
	return JS_FALSE;
    if (*propp)
	return JS_TRUE;

    /*
     * Use the top-level scope from the scope chain, which won't end in the
     * same scope as cx->globalObject for cross-context function calls.
     */
    obj = *objp;
    JS_ASSERT(obj);

    /*
     * Make a top-level variable.
     */
    if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
			     JSPROP_ENUMERATE, &prop)) {
	return JS_FALSE;
    }
    *pobjp = obj;
    *propp = prop;
    return JS_TRUE;
}

JSObject *
js_FindVariableScope(JSContext *cx, JSFunction **funp)
{
    JSStackFrame *fp;
    JSObject *obj, *parent, *withobj;
    JSClass *clasp;
    JSFunction *fun;

    fp = cx->fp;
    withobj = NULL;
    for (obj = fp->scopeChain; ; obj = parent) {
	parent = OBJ_GET_PARENT(cx, obj);
	clasp = OBJ_GET_CLASS(cx, obj);
	if (!parent || clasp != &js_WithClass)
	    break;
	withobj = obj;
    }

    fun = (clasp == &js_FunctionClass) ? JS_GetPrivate(cx, obj) : NULL;
#if JS_HAS_CALL_OBJECT
    if (fun && fun->script) {
	for (; fp && fp->fun != fun; fp = fp->down)
	    ;
	if (fp)
	    obj = js_GetCallObject(cx, fp, parent, withobj);
    } else if (clasp == &js_CallClass) {
	fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
	fun = fp->fun;
    }
#endif

    *funp = fun;
    return obj;
}

JSBool
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
    JSObject *obj2;
    JSScopeProperty *sprop;
    jsint slot;

    if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
	return JS_FALSE;
    if (!sprop) {
	/* Handle old bug that treated empty string as zero index.
	 * Also convert string indices to numbers if applicable. */
	CHECK_FOR_FUNNY_INDEX(id);

#if JS_BUG_NULL_INDEX_PROPS
	/* Indexed properties defaulted to null in old versions. */
	*vp = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
	      ? JSVAL_NULL
	      : JSVAL_VOID;
#else
	*vp = JSVAL_VOID;
#endif

	return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, js_IdToValue(id),
			     vp);
    }

    if (!OBJ_IS_NATIVE(obj2)) {
	OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
	return OBJ_GET_PROPERTY(cx, obj2, id, vp);
    }

    /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
    slot = sprop->slot;
    *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
    JS_UNLOCK_OBJ(cx, obj2);
    if (!SPROP_GET(cx, sprop, obj, obj2, vp)) {
	JS_LOCK_OBJ(cx, obj2);
	OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
	return JS_FALSE;
    }

    if (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_FW_SIMPLE_GET) {
	/* Don't set the property *vp in the prototype (obj2). */
	OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
	return JS_TRUE;
    }


    JS_LOCK_OBJ(cx, obj2);
    LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
    PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj2, id,
			(JSProperty *)sprop);
    OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
    return JS_TRUE;
}

JSBool
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
    JSRuntime *rt;
    JSScope *scope, *protoscope;
    JSScopeProperty *sprop, *protosprop;
    JSHashNumber hash;
    JSSymbol *sym, *protosym;
    JSObject *proto, *tmp, *assignobj;
    jsval protoid;
    JSPropertyOp protogetter, protosetter;
    uintN protoattrs;
    JSClass *clasp;
    jsval pval, aval, rval;
    jsint slot;
    JSErrorReporter older;
    JSString *str;

    rt = cx->runtime;
    JS_LOCK_OBJ(cx, obj);
    protoid = protoattrs = 0;   /* Suppress use-before-set gcc warning */
    protogetter = protosetter = NULL; /* Suppress use-before-set gcc warning */

    scope = js_GetMutableScope(cx, obj);
    if (!scope) {
	JS_UNLOCK_OBJ(cx, obj);
	return JS_FALSE;
    }

    /* Handle old bug that treated empty string as zero index.
     * Also convert string indices to numbers if applicable. */
    CHECK_FOR_FUNNY_INDEX(id);

    hash = js_HashValue(id);
    sym = scope->ops->lookup(cx, scope, id, hash);
    if (sym) {
	sprop = sym_property(sym);
#if JS_HAS_OBJ_WATCHPOINT
	if (!sprop) {
	    uint32 slot, nslots;
	    jsval *slots;

	    /*
	     * Deleted property place-holder, could have a watchpoint that
	     * holds the deleted-but-watched property.  If so, slots may have
	     * shrunk, or at least freeslot may have shrunk due to the delete
	     * operation destroying the property.
	     */
	    sprop = js_FindWatchPoint(rt, obj, js_IdToValue(id));
	    if (sprop && (slot = sprop->slot) >= scope->map.freeslot) {
		if (slot >= scope->map.nslots) {
		    nslots = slot + slot / 2;
		    slots = JS_realloc(cx, obj->slots, nslots * sizeof(jsval));
		    if (!slots) {
			JS_UNLOCK_OBJ(cx, obj);
			return JS_FALSE;
		    }
		    scope->map.nslots = nslots;
		    obj->slots = slots;
		}
		scope->map.freeslot = slot + 1;
	    }
	}
#endif
    } else {
	sprop = NULL;
    }

    if (!sprop) {
	/* Find a prototype property with the same id. */
	proto = LOCKED_OBJ_GET_PROTO(obj);
	protosprop = NULL;

	JS_UNLOCK_OBJ(cx, obj);
	while (proto) {
	    JS_LOCK_OBJ(cx, proto);
	    protoscope = (JSScope *)proto->map;
	    if (MAP_IS_NATIVE(&protoscope->map)) {
		protosym = protoscope->ops->lookup(cx, protoscope, id, hash);
		if (protosym) {
		    protosprop = sym_property(protosym);
		    if (protosprop) {
			protoid = protosprop->id;
			protogetter = protosprop->getter;
			protosetter = protosprop->setter;
			protoattrs = protosprop->attrs;
			JS_UNLOCK_OBJ(cx, proto);
			break;
		    }
		}
	    }
	    tmp = LOCKED_OBJ_GET_PROTO(proto);
	    JS_UNLOCK_OBJ(cx, proto);
	    proto = tmp;
	}
	JS_LOCK_OBJ(cx, obj);

	/* Make a new property descriptor with the right heritage. */
	clasp = LOCKED_OBJ_GET_CLASS(obj);
	if (protosprop) {
	    if (protoattrs & JSPROP_READONLY)
		goto _readonly;
	    sprop = js_NewScopeProperty(cx, scope, id,
					protogetter, protosetter,
					protoattrs);
	    sprop->id = protoid;
	} else {
	    sprop = js_NewScopeProperty(cx, scope, id,
					clasp->getProperty, clasp->setProperty,
					JSPROP_ENUMERATE);
	}
	if (!sprop) {
	    JS_UNLOCK_OBJ(cx, obj);
	    return JS_FALSE;
	}

	/* XXXbe called with obj locked */
	if (!clasp->addProperty(cx, obj, sprop->id, vp)) {
	    js_DestroyScopeProperty(cx, scope, sprop);
	    JS_UNLOCK_OBJ(cx, obj);
	    return JS_FALSE;
	}

	/* Initialize new properties to undefined. */
	LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);

	if (sym) {
	    /* Null-valued symbol left behind from a delete operation. */
	    sym->entry.value = js_HoldScopeProperty(cx, scope, sprop);
	}
    }

    if (!sym) {
	/* Need a new symbol as well as a new property. */
	sym = scope->ops->add(cx, scope, id, sprop);
	if (!sym) {
	    js_DestroyScopeProperty(cx, scope, sprop);
	    JS_UNLOCK_OBJ(cx, obj);
	    return JS_FALSE;
	}
#if JS_BUG_AUTO_INDEX_PROPS
	{
	    jsid id2 = (jsid) INT_TO_JSVAL(sprop->slot - JSSLOT_START);
	    if (!scope->ops->add(cx, scope, id2, sprop)) {
		scope->ops->remove(cx, scope, id);
		JS_UNLOCK_OBJ(cx, obj);
		return JS_FALSE;
	    }
	    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2,
				(JSProperty *)sprop);
	}
#endif
	PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
			    (JSProperty *)sprop);
    }

    /* Get the current property value from its slot. */
    JS_ASSERT(sprop->slot < obj->map->freeslot);
    slot = sprop->slot;
    pval = LOCKED_OBJ_GET_SLOT(obj, slot);

    /* Evil overloaded operator assign() hack. */
    if ((!JSVERSION_IS_ECMA(cx->version)) && (JSVAL_IS_OBJECT(pval))) {
	assignobj = JSVAL_TO_OBJECT(pval);
	if (assignobj) {
	    older = JS_SetErrorReporter(cx, NULL);
	    JS_UNLOCK_OBJ(cx, obj);
	    if (OBJ_GET_PROPERTY(cx, assignobj, (jsid)rt->atomState.assignAtom,
				 &aval) &&
		JSVAL_IS_FUNCTION(cx, aval) &&
		js_CallFunctionValue(cx, assignobj, aval, 1, vp, &rval))
	    {
		*vp = rval;
		JS_SetErrorReporter(cx, older);
		sprop->attrs |= JSPROP_ASSIGNHACK;
		return JS_TRUE;
	    }
	    JS_SetErrorReporter(cx, older);
	    JS_LOCK_OBJ(cx, obj);
	}
    }

    /* Check for readonly *after* the assign() hack. */
    if (sprop->attrs & JSPROP_READONLY) {

/* "readonly" can be a language extension on OSF */
_readonly:
	JS_UNLOCK_OBJ(cx, obj);
	if (JSVERSION_IS_ECMA(cx->version))
	    return JS_TRUE;
	str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL);
	if (str)
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_READ_ONLY, JS_GetStringBytes(str));
	return JS_FALSE;
    }

#ifdef JS_THREADSAFE
    /* Hold sprop across setter callout, and drop after, in case of delete. */
    sprop->nrefs++;
#endif

    /* Avoid deadlock by unlocking obj while calling sprop's setter. */
    JS_UNLOCK_OBJ(cx, obj);

    /* Let the setter modify vp before copying from it to obj->slots[slot]. */
    if (!SPROP_SET(cx, sprop, obj, obj, vp)) {
#ifdef JS_THREADSAFE
	JS_LOCK_OBJ_VOID(cx, obj, js_DropScopeProperty(cx, scope, sprop));
#endif
	return JS_FALSE;
    }

    /* Relock obj until we are done with sprop. */
    JS_LOCK_OBJ(cx, obj);

#ifdef JS_THREADSAFE
    sprop = js_DropScopeProperty(cx, scope, sprop);
    if (!sprop) {
	/* Lost a race with someone who deleted sprop. */
	JS_UNLOCK_OBJ(cx, obj);
	return JS_TRUE;
    }
#endif
    GC_POKE(cx, pval);
    LOCKED_OBJ_SET_SLOT(obj, slot, *vp);

#if JS_BUG_SET_ENUMERATE
    /* Setting a property makes it enumerable. */
    sprop->attrs |= JSPROP_ENUMERATE;
#endif
    JS_UNLOCK_OBJ(cx, obj);
    return JS_TRUE;
}

JSBool
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
		 uintN *attrsp)
{
    JSBool noprop, ok;
    JSScopeProperty *sprop;

    noprop = !prop;
    if (noprop) {
	if (!js_LookupProperty(cx, obj, id, &obj, &prop))
	    return JS_FALSE;
	if (!prop)
	    return JS_TRUE;
	if (!OBJ_IS_NATIVE(obj)) {
	    ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
	    OBJ_DROP_PROPERTY(cx, obj, prop);
	    return ok;
	}
    }
    sprop = (JSScopeProperty *)prop;
    *attrsp = sprop->attrs;
    if (noprop)
	OBJ_DROP_PROPERTY(cx, obj, prop);
    return JS_TRUE;
}

JSBool
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
		 uintN *attrsp)
{
    JSBool noprop, ok;
    JSScopeProperty *sprop;

    noprop = !prop;
    if (noprop) {
	if (!js_LookupProperty(cx, obj, id, &obj, &prop))
	    return JS_FALSE;
	if (!prop)
	    return JS_TRUE;
	if (!OBJ_IS_NATIVE(obj)) {
	    ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
	    OBJ_DROP_PROPERTY(cx, obj, prop);
	    return ok;
	}
    }
    sprop = (JSScopeProperty *)prop;
    sprop->attrs = *attrsp;
    if (noprop)
	OBJ_DROP_PROPERTY(cx, obj, prop);
    return JS_TRUE;
}

JSBool
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
{
#if JS_HAS_PROP_DELETE
    JSRuntime *rt;
    JSObject *proto;
    JSProperty *prop;
    JSScopeProperty *sprop;
    JSString *str;
    JSScope *scope;
    JSSymbol *sym;

    rt = cx->runtime;

    *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;

    /* Handle old bug that treated empty string as zero index.
     * Also convert string indices to numbers if applicable. */
    CHECK_FOR_FUNNY_INDEX(id);

    if (!js_LookupProperty(cx, obj, id, &proto, &prop))
	return JS_FALSE;
    if (!prop || proto != obj) {
	if (prop)
	    OBJ_DROP_PROPERTY(cx, proto, prop);
	/*
	 * If no property, or the property comes from a prototype, call the
	 * class's delProperty hook with rval as the result parameter.
	 */
	return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, js_IdToValue(id),
						   rval);
    }

    sprop = (JSScopeProperty *)prop;
    if (sprop->attrs & JSPROP_PERMANENT) {
	OBJ_DROP_PROPERTY(cx, obj, prop);
	if (JSVERSION_IS_ECMA(cx->version)) {
	    *rval = JSVAL_FALSE;
	    return JS_TRUE;
	}
	str = js_DecompileValueGenerator(cx, js_IdToValue(id), NULL);
	if (str) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_PERMANENT, JS_GetStringBytes(str));
	}
	return JS_FALSE;
    }

    /* XXXbe called with obj locked */
    if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, sprop->id, rval)) {
	OBJ_DROP_PROPERTY(cx, obj, prop);
	return JS_FALSE;
    }

    GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
    scope = (JSScope *)obj->map;

    /*
     * Purge cache only if prop is not about to be destroyed (since
     * js_DestroyScopeProperty purges for us).
     */
    if (sprop->nrefs != 1) {
	PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
			    PROP_NOT_FOUND(obj, id));
    }

#if JS_HAS_OBJ_WATCHPOINT
    if (sprop->setter == js_watch_set) {
	/*
	 * Keep the symbol around with null value in case of re-set.
	 * The watchpoint will hold the "deleted" property until it
	 * is removed by obj_unwatch or a native JS_ClearWatchPoint.
	 * See js_SetProperty for the re-set logic.
	 */
	for (sym = sprop->symbols; sym; sym = sym->next) {
	    if (sym_id(sym) == id) {
		sym->entry.value = NULL;
		sprop = js_DropScopeProperty(cx, scope, sprop);
		JS_ASSERT(sprop);
		goto out;
	    }
	}
    }
#endif /* JS_HAS_OBJ_WATCHPOINT */

    scope->ops->remove(cx, scope, id);
out:
    OBJ_DROP_PROPERTY(cx, obj, prop);
    return JS_TRUE;
#else  /* !JS_HAS_PROP_DELETE */
    jsval null = JSVAL_NULL;

    *rval = JSVAL_VOID;
    return js_SetProperty(cx, obj, id, &null);
#endif /* !JS_HAS_PROP_DELETE */
}

JSBool
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
{
    jsval v;
    JSString *str;

    v = OBJECT_TO_JSVAL(obj);
    switch (hint) {
      case JSTYPE_STRING:
        /*
         * Propagate the exception if js_TryMethod finds an appropriate
         * method, and calling that method returned failure.
         */
        if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
                          &v))
            return JS_FALSE;

	if (!JSVAL_IS_PRIMITIVE(v)) {
	    if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
		return JS_FALSE;

	    /*
	     * JS1.2 never failed (except for malloc failure) to convert an
	     * object to a string.  ECMA requires an error if both toString
	     * and valueOf fail to produce a primitive value.
	     */
	    if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
		char *bytes = JS_smprintf("[object %s]",
					  OBJ_GET_CLASS(cx, obj)->name);
		if (!bytes)
		    return JS_FALSE;
		str = JS_NewString(cx, bytes, strlen(bytes));
		if (!str) {
		    free(bytes);
		    return JS_FALSE;
		}
		v = STRING_TO_JSVAL(str);
		goto out;
	    }
	}
	break;

      default:
	if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
	    return JS_FALSE;
	if (!JSVAL_IS_PRIMITIVE(v)) {
	    if (JS_TypeOfValue(cx, v) == hint)
		goto out;
	    /* Don't convert to string (source object literal) for JS1.2. */
	    if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
		goto out;
	    if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
                              NULL, &v))
                return JS_FALSE;
	}
	break;
    }
    if (!JSVAL_IS_PRIMITIVE(v)) {
	/* Avoid recursive death through js_DecompileValueGenerator. */
	if (hint == JSTYPE_STRING) {
	    str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
	    if (!str)
		return JS_FALSE;
	} else {
	    str = NULL;
	}
	*vp = OBJECT_TO_JSVAL(obj);
	str = js_DecompileValueGenerator(cx, v, str);
	if (str) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_CANT_CONVERT_TO,
				 JS_GetStringBytes(str),
				 (hint == JSTYPE_VOID)
				 ? "primitive type"
				 : js_type_str[hint]);
	}
	return JS_FALSE;
    }
out:
    *vp = v;
    return JS_TRUE;
}

extern JSIdArray *
js_NewIdArray(JSContext *cx, jsint length)
{
    JSIdArray *ida;

    ida = JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
    if (ida)
	ida->length = length;
    return ida;
}

/* Private type used to iterate over all properties of a native JS object */
typedef struct JSNativeIteratorState {
    jsint next_index;   /* index into jsid array */
    JSIdArray *ida;     /* All property ids in enumeration */
} JSNativeIteratorState;

/*
 * This function is used to enumerate the properties of native JSObjects
 * and those host objects that do not define a JSNewEnumerateOp-style iterator
 * function.
 */
JSBool
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
	     jsval *statep, jsid *idp)
{
    JSObject *proto_obj;
    JSClass *clasp;
    JSEnumerateOp enumerate;
    JSScopeProperty *sprop;
    jsint i, length;
    JSScope *scope;
    JSIdArray *ida;
    JSNativeIteratorState *state;

    clasp = OBJ_GET_CLASS(cx, obj);
    enumerate = clasp->enumerate;
    if (clasp->flags & JSCLASS_NEW_ENUMERATE)
	return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);

    switch (enum_op) {

    case JSENUMERATE_INIT:
	if (!enumerate(cx, obj))
	    goto init_error;
	length = 0;

	/*
	 * The set of all property ids is pre-computed when the iterator
	 * is initialized so as to avoid problems with properties being
	 * deleted during the iteration.
	 */
	JS_LOCK_OBJ(cx, obj);
	scope = (JSScope *) obj->map;

	/*
	 * If this object shares a scope with its prototype, don't enumerate
	 * its properties.  Otherwise they will be enumerated a second time
	 * when the prototype object is enumerated.
	 */
	proto_obj = OBJ_GET_PROTO(cx, obj);
	if (proto_obj && (scope == (JSScope *)proto_obj->map)) {
	    ida = js_NewIdArray(cx, 0);
	    if (!ida) {
	      JS_UNLOCK_OBJ(cx, obj);
	      goto init_error;
	    }
	} else {
	    /* Object has a private scope; Enumerate all props in scope. */
	    for (sprop = scope->props; sprop; sprop = sprop->next) {
		if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols)
		    length++;
	    }
	    ida = js_NewIdArray(cx, length);
	    if (!ida) {
		JS_UNLOCK_OBJ(cx, obj);
		goto init_error;
	    }
	    i = 0;
	    for (sprop = scope->props; sprop; sprop = sprop->next) {
		if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) {
		    JS_ASSERT(i < length);
		    ida->vector[i++] = sym_id(sprop->symbols);
		}
	    }
	}
	JS_UNLOCK_OBJ(cx, obj);

	state = JS_malloc(cx, sizeof(JSNativeIteratorState));
	if (!state) {
	    JS_DestroyIdArray(cx, ida);
	    goto init_error;
	}
	state->ida = ida;
	state->next_index = 0;
	*statep = PRIVATE_TO_JSVAL(state);
	if (idp)
	    *idp = INT_TO_JSVAL(length);
	return JS_TRUE;

    case JSENUMERATE_NEXT:
	state = JSVAL_TO_PRIVATE(*statep);
	ida = state->ida;
	length = ida->length;
	if (state->next_index != length) {
	    *idp = ida->vector[state->next_index++];
	    return JS_TRUE;
	}

	/* Fall through ... */

    case JSENUMERATE_DESTROY:
	state = JSVAL_TO_PRIVATE(*statep);
	JS_DestroyIdArray(cx, state->ida);
	JS_free(cx, state);
	*statep = JSVAL_NULL;
	return JS_TRUE;

    default:
	JS_ASSERT(0);
	return JS_FALSE;
    }

init_error:
    *statep = JSVAL_NULL;
    return JS_FALSE;
}

JSBool
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
	       jsval *vp, uintN *attrsp)
{
    JSObject *pobj;
    JSProperty *prop;
    JSScopeProperty *sprop;
    JSClass *clasp;
    JSBool ok;

    if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
	return JS_FALSE;
    if (!prop) {
	*vp = JSVAL_VOID;
	*attrsp = 0;
	return JS_TRUE;
    }
    if (!OBJ_IS_NATIVE(pobj)) {
	OBJ_DROP_PROPERTY(cx, pobj, prop);
	return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
    }
    sprop = (JSScopeProperty *)prop;
    *vp = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
    *attrsp = sprop->attrs;
    clasp = LOCKED_OBJ_GET_CLASS(obj);
    if (clasp->checkAccess) {
	JS_UNLOCK_OBJ(cx, pobj);
	ok = clasp->checkAccess(cx, obj, sprop->id, mode, vp);
	JS_LOCK_OBJ(cx, pobj);
    } else {
	ok = JS_TRUE;
    }
    OBJ_DROP_PROPERTY(cx, pobj, prop);
    return ok;
}

JSBool
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSClass *clasp;

    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
    if (!clasp->call) {
	js_ReportIsNotFunction(cx, &argv[-2], JS_FALSE);
	return JS_FALSE;
    }
    return clasp->call(cx, obj, argc, argv, rval);
}

JSBool
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	     jsval *rval)
{
    JSClass *clasp;

    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
    if (!clasp->construct) {
	js_ReportIsNotFunction(cx, &argv[-2], JS_TRUE);
	return JS_FALSE;
    }
    return clasp->construct(cx, obj, argc, argv, rval);
}

JSBool
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
{
    JSClass *clasp;

    clasp = OBJ_GET_CLASS(cx, obj);
    if (clasp->hasInstance)
	return clasp->hasInstance(cx, obj, v, bp);
    *bp = JS_FALSE;
    return JS_TRUE;
}

JSBool
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
{
    JSObject *obj2;

    *bp = JS_FALSE;
    if (JSVAL_IS_PRIMITIVE(v))
	return JS_TRUE;
    obj2 = JSVAL_TO_OBJECT(v);
    do {
	if (obj2 == obj) {
	    *bp = JS_TRUE;
	    break;
	}
    } while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL);
    return JS_TRUE;
}

#ifdef JS_THREADSAFE
void
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
{
    js_DropScopeProperty(cx, (JSScope *)obj->map, (JSScopeProperty *)prop);
    JS_UNLOCK_OBJ(cx, obj);
}
#endif

JSBool
js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
{
    jsval v;
    JSObject *ctor;

    if (!FindConstructor(cx, name, &v))
	return JS_FALSE;
    if (JSVAL_IS_FUNCTION(cx, v)) {
	ctor = JSVAL_TO_OBJECT(v);
	if (!OBJ_GET_PROPERTY(cx, ctor,
			      (jsid)cx->runtime->atomState.classPrototypeAtom,
			      &v)) {
	    return JS_FALSE;
	}
    }
    *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
    return JS_TRUE;
}

JSBool
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
		     uintN attrs)
{
    /*
     * Use the given attributes for the prototype property of the constructor,
     * as user-defined constructors have a DontEnum prototype (it can be reset
     * or even deleted), while native "system" constructors require DontEnum |
     * ReadOnly | DontDelete.
     */
    if (!OBJ_DEFINE_PROPERTY(cx, ctor,
			     (jsid)cx->runtime->atomState.classPrototypeAtom,
			     OBJECT_TO_JSVAL(proto), NULL, NULL,
			     attrs, NULL)) {
	return JS_FALSE;
    }

    /*
     * ECMA says that Object.prototype.constructor, or f.prototype.constructor
     * for a user-defined function f, is DontEnum.
     */
    return OBJ_DEFINE_PROPERTY(cx, proto,
			       (jsid)cx->runtime->atomState.constructorAtom,
			       OBJECT_TO_JSVAL(ctor), NULL, NULL,
			       0, NULL);
}

JSBool
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
{
    JSObject *obj;

    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
	obj = NULL;
    } else if (JSVAL_IS_OBJECT(v)) {
	obj = JSVAL_TO_OBJECT(v);
	if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
	    return JS_FALSE;
	if (JSVAL_IS_OBJECT(v))
	    obj = JSVAL_TO_OBJECT(v);
    } else {
	if (JSVAL_IS_STRING(v)) {
	    obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
	} else if (JSVAL_IS_INT(v)) {
	    obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
	} else if (JSVAL_IS_DOUBLE(v)) {
	    obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
	} else {
	    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
	    obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
	}
	if (!obj)
	    return JS_FALSE;
    }
    *objp = obj;
    return JS_TRUE;
}

JSObject *
js_ValueToNonNullObject(JSContext *cx, jsval v)
{
    JSObject *obj;
    JSString *str;

    if (!js_ValueToObject(cx, v, &obj))
	return NULL;
    if (!obj) {
	str = js_DecompileValueGenerator(cx, v, NULL);
	if (str) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
	}
    }
    return obj;
}

JSBool
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
{
#if JS_HAS_VALUEOF_HINT
    jsval argv[1];

    argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
                        rval);
#else
    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
                        rval);
#endif
}

JSBool
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
	     uintN argc, jsval *argv, jsval *rval)
{
    JSErrorReporter older;
    jsval fval;
    JSBool ok;

    /*
     * Report failure only if an appropriate method was found, and calling it
     * returned failure.  We propagate failure in this case to make exceptions
     * behave properly.
     */

    older = JS_SetErrorReporter(cx, NULL);
    if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) &&
	JSVAL_IS_OBJECT(fval) &&
	fval != JSVAL_NULL) {
	ok = js_CallFunctionValue(cx, obj, fval, argc, argv, rval);
    } else {
        ok = JS_TRUE;
    }
    JS_SetErrorReporter(cx, older);
    return ok;
}

#if JS_HAS_XDR

#include "jsxdrapi.h"

JSBool
js_XDRObject(JSXDRState *xdr, JSObject **objp)
{
    JSContext *cx;
    JSClass *clasp;
    char *className;
    uint32 classId, classDef;
    JSBool ok;
    JSObject *proto;

    cx = xdr->cx;
    if (xdr->mode == JSXDR_ENCODE) {
	clasp = OBJ_GET_CLASS(cx, *objp);
	className = clasp->name;
	classId = JS_FindClassIdByName(xdr, className);
	classDef = !classId;
	if (classDef && !JS_RegisterClass(xdr, clasp, &classId))
	    return JS_FALSE;
    } else {
	classDef = 0;
	className = NULL;
        clasp = NULL;           /* quell GCC overwarning */
    }

    /* XDR a flag word followed (if true) by the class name. */
    if (!JS_XDRUint32(xdr, &classDef))
	return JS_FALSE;
    if (classDef && !JS_XDRCString(xdr, &className))
	return JS_FALSE;

    /* From here on, return through out: to free className if it was set. */
    ok = JS_XDRUint32(xdr, &classId);
    if (!ok)
	goto out;

    if (xdr->mode != JSXDR_ENCODE) {
	if (classDef) {
	    ok = js_GetClassPrototype(cx, className, &proto);
	    if (!ok)
		goto out;
	    clasp = OBJ_GET_CLASS(cx, proto);
	    ok = JS_RegisterClass(xdr, clasp, &classId);
	    if (!ok)
		goto out;
	} else {
	    clasp = JS_FindClassById(xdr, classId);
	    if (!clasp) {
		char numBuf[12];
		JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_CANT_FIND_CLASS, numBuf);
		ok = JS_FALSE;
		goto out;
	    }
	}
    }

    if (!clasp->xdrObject) {
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_CANT_XDR_CLASS, clasp->name);
	ok = JS_FALSE;
    } else {
	ok = clasp->xdrObject(xdr, objp);
    }
out:
    if (xdr->mode != JSXDR_ENCODE && className)
	JS_free(cx, className);
    return ok;
}

#endif /* JS_HAS_XDR */


#ifdef DEBUG

/* Routines to print out values during debugging. */

static void printChar(jschar *cp) {
    fprintf(stderr, "jschar* (0x%p) \"", cp);
    while (*cp)
	fputc(*cp++, stderr);
    fputc('"', stderr);
    fputc('\n', stderr);
}

static void printString(JSString *str) {
    jsuint i;
    fprintf(stderr, "string (0x%p) \"", str);
    for (i=0; i < str->length; i++)
	fputc(str->chars[i], stderr);
    fputc('"', stderr);
    fputc('\n', stderr);
}

static void printVal(jsval val);

static void printObj(JSObject *jsobj) { 
    jsuint i;
    jsval val;
    JSClass *clasp;

    fprintf(stderr, "object 0x%p\n", jsobj);
    clasp = OBJ_GET_CLASS(NULL, jsobj);
    fprintf(stderr, "class 0x%p %s\n", clasp, clasp->name);
    for (i=0; i < jsobj->map->nslots; i++) {
        fprintf(stderr, "slot %3d ", i);
        val = jsobj->slots[i];
        if (JSVAL_IS_OBJECT(val))
	    fprintf(stderr, "object 0x%p\n", JSVAL_TO_OBJECT(val));
        else
            printVal(val);
    }
}

static void printVal(jsval val) {
    fprintf(stderr, "val %d (0x%p) = ", val, val);
    if (JSVAL_IS_NULL(val)) {
	fprintf(stderr, "null\n");
    } else if (JSVAL_IS_VOID(val)) {
	fprintf(stderr, "undefined\n");
    } else if (JSVAL_IS_OBJECT(val)) {
        printObj(JSVAL_TO_OBJECT(val));
    } else if (JSVAL_IS_INT(val)) {
	fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
    } else if (JSVAL_IS_STRING(val)) {
	printString(JSVAL_TO_STRING(val));
    } else if (JSVAL_IS_DOUBLE(val)) {
	fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
    } else {
	JS_ASSERT(JSVAL_IS_BOOLEAN(val));
	fprintf(stderr, "(boolean) %s\n",
		JSVAL_TO_BOOLEAN(val) ? "true" : "false");
    }
    fflush(stderr);
}

static void printId(jsid id) {
    fprintf(stderr, "id %d (0x%p) is ", id, id);
    printVal(js_IdToValue(id));
}

static void printAtom(JSAtom *atom) {
    printString(ATOM_TO_STRING(atom));
}

#endif


**** End of jsobj.c. ****

**** Start of jsobj.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsobj_h___
#define jsobj_h___
/*
 * JS object definitions.
 *
 * A JS object consists of a possibly-shared object descriptor containing
 * ordered property names, called the map; and a dense vector of property
 * values, called slots.  The map/slot pointer pair is GC'ed, while the map
 * is reference counted and the slot vector is malloc'ed.
 */
#include "jshash.h" /* Added by JSIFY */
#include "jsprvtd.h"
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

struct JSObjectMap {
    jsrefcount  nrefs;          /* count of all referencing objects */
    JSObjectOps *ops;           /* high level object operation vtable */
    uint32      nslots;         /* length of obj->slots vector */
    uint32      freeslot;       /* index of next free obj->slots element */
};

/* Shorthand macros for frequently-made calls. */
#if defined JS_THREADSAFE && defined DEBUG
#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp)                             \
    (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__)
#else
#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp)                             \
    (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp)
#endif
#define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp)        \
    (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp)
#define OBJ_GET_PROPERTY(cx,obj,id,vp)                                        \
    (obj)->map->ops->getProperty(cx,obj,id,vp)
#define OBJ_SET_PROPERTY(cx,obj,id,vp)                                        \
    (obj)->map->ops->setProperty(cx,obj,id,vp)
#define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp)                             \
    (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp)
#define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp)                             \
    (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp)
#define OBJ_DELETE_PROPERTY(cx,obj,id,rval)                                   \
    (obj)->map->ops->deleteProperty(cx,obj,id,rval)
#define OBJ_DEFAULT_VALUE(cx,obj,hint,vp)                                     \
    (obj)->map->ops->defaultValue(cx,obj,hint,vp)
#define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp)                                                 \
    (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp)
#define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp)                            \
    (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp)

/* These two are time-optimized to avoid stub calls. */
#define OBJ_THIS_OBJECT(cx,obj)                                               \
    ((obj)->map->ops->thisObject                                              \
     ? (obj)->map->ops->thisObject(cx,obj)                                    \
     : (obj))
#define OBJ_DROP_PROPERTY(cx,obj,prop)                                        \
    ((obj)->map->ops->dropProperty                                            \
     ? (obj)->map->ops->dropProperty(cx,obj,prop)                             \
     : (void)0)

struct JSObject {
    JSObjectMap *map;
    jsval       *slots;
};

#define JSSLOT_PROTO        0
#define JSSLOT_PARENT       1
#define JSSLOT_CLASS        2
#define JSSLOT_PRIVATE      3
#define JSSLOT_START        3

#define JSSLOT_FREE(clasp)  (((clasp)->flags & JSCLASS_HAS_PRIVATE)           \
			     ? JSSLOT_PRIVATE + 1                             \
			     : JSSLOT_START)

#define JS_INITIAL_NSLOTS   5

#ifdef DEBUG
#define MAP_CHECK_SLOT(map,slot) \
    JS_ASSERT((uint32)slot < JS_MAX((map)->nslots, (map)->freeslot))
#define OBJ_CHECK_SLOT(obj,slot) \
    MAP_CHECK_SLOT((obj)->map, slot)
#else
#define OBJ_CHECK_SLOT(obj,slot) ((void)0)
#endif

/* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */
#define LOCKED_OBJ_GET_SLOT(obj,slot) \
    (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot])
#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \
    (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value))
#define LOCKED_OBJ_GET_PROTO(obj) \
    JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO))
#define LOCKED_OBJ_GET_CLASS(obj) \
    ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS)))

#ifdef JS_THREADSAFE

/* Thread-safe functions and wrapper macros for accessing obj->slots. */
#define OBJ_GET_SLOT(cx,obj,slot) \
    (OBJ_CHECK_SLOT(obj, slot), js_GetSlotWhileLocked(cx, obj, slot))
#define OBJ_SET_SLOT(cx,obj,slot,value) \
    (OBJ_CHECK_SLOT(obj, slot), js_SetSlotWhileLocked(cx, obj, slot, value))

#else   /* !JS_THREADSAFE */

#define OBJ_GET_SLOT(cx,obj,slot)       LOCKED_OBJ_GET_SLOT(obj,slot)
#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value)

#endif /* !JS_THREADSAFE */

/* Thread-safe proto, parent, and class access macros. */
#define OBJ_GET_PROTO(cx,obj) \
    JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO))
#define OBJ_SET_PROTO(cx,obj,proto) \
    OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto))

#define OBJ_GET_PARENT(cx,obj) \
    JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT))
#define OBJ_SET_PARENT(cx,obj,parent) \
    OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent))

#define OBJ_GET_CLASS(cx,obj) \
    ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS)))

/* Test whether a map or object is native. */
#define MAP_IS_NATIVE(map)  ((map)->ops == &js_ObjectOps || \
                             (map)->ops == &js_WithObjectOps)
#define OBJ_IS_NATIVE(obj)  MAP_IS_NATIVE((obj)->map)

extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;
extern JSClass      js_ObjectClass;
extern JSClass      js_WithClass;

struct JSSharpObjectMap {
    jsrefcount  depth;
    jsatomid    sharpgen;
    JSHashTable *table;
};

#define SHARP_BIT       1
#define IS_SHARP(he)	((jsatomid)(he)->value & SHARP_BIT)
#define MAKE_SHARP(he)  ((he)->value = (void*)((jsatomid)(he)->value|SHARP_BIT))

extern JSHashEntry *
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
		    jschar **sp);

extern void
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap);

extern JSBool
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval);

extern JSBool
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval);

extern JSObject *
js_InitObjectClass(JSContext *cx, JSObject *obj);

extern void
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
		 JSClass *clasp);

extern JSObjectMap *
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
		JSClass *clasp, JSObject *obj);

extern void
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map);

extern JSObjectMap *
js_HoldObjectMap(JSContext *cx, JSObjectMap *map);

extern JSObjectMap *
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj);

extern JSObject *
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);

extern JSObject *
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
		   JSObject *parent);

extern void
js_FinalizeObject(JSContext *cx, JSObject *obj);

extern JSBool
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp);

extern void
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot);

/*
 * On error, return false.  On success, if propp is non-null, return true with
 * obj locked and with a held property in *propp; if propp is null, return true
 * but release obj's lock first.  Therefore all callers who pass non-null propp
 * result parameters must later call OBJ_DROP_PROPERTY(cx, obj, *propp) both to
 * drop the held property, and to release the lock on obj.
 */
extern JSBool
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
		  JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
		  JSProperty **propp);

/*
 * Unlike js_DefineProperty, propp must be non-null.  On success, and if id was
 * found, return true with *objp non-null and locked, and with a held property
 * stored in *propp.  If successful but id was not found, return true with both
 * *objp and *propp null.  Therefore all callers who receive a non-null *propp
 * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp).
 */
#if defined JS_THREADSAFE && defined DEBUG
extern JS_FRIEND_API(JSBool)
_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
		   JSProperty **propp, const char *file, uintN line);

#define js_LookupProperty(cx,obj,id,objp,propp) \
    _js_LookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__)
#else
extern JS_FRIEND_API(JSBool)
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
		  JSProperty **propp);
#endif

extern JS_FRIEND_API(JSBool)
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
		JSProperty **propp);

extern JSBool
js_FindVariable(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
		JSProperty **propp);

extern JSObject *
js_FindVariableScope(JSContext *cx, JSFunction **funp);

extern JSBool
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);

extern JSBool
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);

extern JSBool
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
		 uintN *attrsp);

extern JSBool
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
		 uintN *attrsp);

extern JSBool
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval);

extern JSBool
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp);

extern JSBool
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
	     jsval *statep, jsid *idp);

extern JSBool
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
	       jsval *vp, uintN *attrsp);

extern JSBool
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

extern JSBool
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	     jsval *rval);

extern JSBool
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);

extern JSBool
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);

extern JSBool
js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop);

extern JSBool
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
		     uintN attrs);

extern JSBool
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp);

extern JSObject *
js_ValueToNonNullObject(JSContext *cx, jsval v);

extern JSBool
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval);

extern JSBool
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
	     uintN argc, jsval *argv, jsval *rval);

extern JSBool
js_XDRObject(JSXDRState *xdr, JSObject **objp);

extern JSIdArray *
js_NewIdArray(JSContext *cx, jsint length);

JS_END_EXTERN_C

#endif /* jsobj_h___ */

**** End of jsobj.h. ****

**** Start of jsopcode.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS bytecode descriptors, disassemblers, and decompilers.
 */
#include "jsstddef.h"
#include <memory.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsdtoa.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

#ifdef _WINDOWS
#pragma optimize( "", off )
#endif

char js_in_str[]            = "in";
char js_instanceof_str[]    = "instanceof";
char js_new_str[]           = "new";
char js_delete_str[]        = "delete";
char js_typeof_str[]        = "typeof";
char js_void_str[]          = "void";
char js_null_str[]          = "null";
char js_this_str[]          = "this";
char js_false_str[]         = "false";
char js_true_str[]          = "true";

char *js_incop_str[]        = {"++", "--"};

/* Pollute the namespace locally for MSVC Win16, but not for WatCom.  */
#ifdef __WINDOWS_386__
    #ifdef FAR
	#undef FAR
    #endif
#else  /* !__WINDOWS_386__ */
#ifndef FAR
#define FAR
#endif
#endif /* !__WINDOWS_386__ */

JSCodeSpec FAR js_CodeSpec[] = {
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
    {name,token,length,nuses,ndefs,prec,format},
#include "jsopcode.tbl"
#undef OPDEF
};

uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];

/************************************************************************/

#ifdef DEBUG

JS_FRIEND_API(void)
js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
{
    jsbytecode *pc, *end;
    uintN len;

    pc = script->code;
    end = pc + script->length;
    while (pc < end) {
	len = js_Disassemble1(cx, script, pc, pc - script->code, lines, fp);
	if (!len)
	    return;
	pc += len;
    }
}

JS_FRIEND_API(uintN)
js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
		JSBool lines, FILE *fp)
{
    JSOp op;
    JSCodeSpec *cs;
    intN len, off;
    JSAtom *atom;
    JSString *str;
    char *cstr;

    op = (JSOp)*pc;
    if (op >= JSOP_LIMIT) {
	char numBuf1[12], numBuf2[12];
	JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
	JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
	return 0;
    }
    cs = &js_CodeSpec[op];
    len = (intN)cs->length;
    fprintf(fp, "%05u:", loc);
    if (lines)
	fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
    fprintf(fp, "  %s", cs->name);
    switch (cs->format & JOF_TYPEMASK) {
      case JOF_BYTE:
	if (op == JSOP_TRAP) {
	    op = JS_GetTrapOpcode(cx, script, pc);
	    if (op == JSOP_LIMIT)
		return 0;
	    len = (intN)js_CodeSpec[op].length;
	}
	break;

      case JOF_JUMP:
	off = GET_JUMP_OFFSET(pc);
	fprintf(fp, " %u (%d)", loc + off, off);
	break;

      case JOF_CONST:
	atom = GET_ATOM(cx, script, pc);
	str = js_ValueToString(cx, ATOM_KEY(atom));
	if (!str)
	    return 0;
	cstr = js_DeflateString(cx, str->chars, str->length);
	if (!cstr)
	    return 0;
	fprintf(fp, (op == JSOP_STRING) ? " \"%s\"" : " %s", cstr);
	JS_free(cx, cstr);
	break;

      case JOF_UINT16:
	fprintf(fp, " %u", GET_ARGC(pc));
	break;

#if JS_HAS_SWITCH_STATEMENT
      case JOF_TABLESWITCH:
      {
	jsbytecode *pc2, *end;
	jsint i, low, high;

	pc2 = pc;
	off = GET_JUMP_OFFSET(pc2);
	end = pc + off;
	pc2 += JUMP_OFFSET_LEN;
	low = GET_JUMP_OFFSET(pc2);
	pc2 += JUMP_OFFSET_LEN;
	high = GET_JUMP_OFFSET(pc2);
	pc2 += JUMP_OFFSET_LEN;
	fprintf(fp, " defaultOffset %d low %d high %d", off, low, high);
	if (pc2 + 1 < end) {
	    for (i = low; i <= high; i++) {
		off = GET_JUMP_OFFSET(pc2);
		fprintf(fp, "\n\t%d: %d", i, off);
		pc2 += JUMP_OFFSET_LEN;
	    }
	}
	len = 1 + pc2 - pc;
	break;
      }

      case JOF_LOOKUPSWITCH:
      {
	jsbytecode *pc2 = pc;
	uintN npairs;
	jsval key;

	off = GET_JUMP_OFFSET(pc2);
	pc2 += JUMP_OFFSET_LEN;
	npairs = (uintN) GET_ATOM_INDEX(pc2);
	pc2 += ATOM_INDEX_LEN;
	fprintf(fp, " offset %d npairs %u", off, npairs);
	while (npairs) {
	    atom = GET_ATOM(cx, script, pc2);
	    pc2 += ATOM_INDEX_LEN;
	    off = GET_JUMP_OFFSET(pc2);
	    pc2 += JUMP_OFFSET_LEN;

	    key = ATOM_KEY(atom);
	    str = js_ValueToString(cx, key);
	    if (!str)
		return 0;
	    cstr = js_DeflateString(cx, str->chars, str->length);
	    if (!cstr)
		return 0;
	    if (JSVAL_IS_STRING(key))
		fprintf(fp, "\n\t\"%s\": %d", cstr, off);
	    else
		fprintf(fp, "\n\t%s: %d", cstr, off);
	    JS_free(cx, cstr);
	    npairs--;
	}
	len = 1 + pc2 - pc;
	break;
      }
#endif /* JS_HAS_SWITCH_STATEMENT */

      case JOF_QARG:
	fprintf(fp, " %u", GET_ARGNO(pc));
	break;

      case JOF_QVAR:
	fprintf(fp, " %u", GET_VARNO(pc));
	break;

      default: {
	char numBuf[12];
	JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			     JSMSG_UNKNOWN_FORMAT, numBuf);
	return 0;
      }
    }
    fputs("\n", fp);
    return len;
}

#endif /* DEBUG */

/************************************************************************/

/*
 * Sprintf, but with unlimited and automatically allocated buffering.
*/
typedef struct Sprinter {
    JSContext       *context;       /* context executing the decompiler */
    JSArenaPool     *pool;          /* string allocation pool */
    char            *base;          /* base address of buffer in pool */
    size_t          size;           /* size of buffer allocated at base */
    ptrdiff_t       offset;         /* offset of next free char in buffer */
} Sprinter;

#define INIT_SPRINTER(cx, sp, ap, off) \
    ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
     (sp)->offset = off)

#define OFF2STR(sp,off) ((sp)->base + (off))
#define STR2OFF(sp,str) ((str) - (sp)->base)

static JSBool
SprintAlloc(Sprinter *sp, size_t nb)
{
    if (!sp->base) {
	JS_ARENA_ALLOCATE(sp->base, sp->pool, nb);
    } else {
	JS_ARENA_GROW(sp->base, sp->pool, sp->size, nb);
    }
    if (!sp->base) {
	JS_ReportOutOfMemory(sp->context);
	return JS_FALSE;
    }
    sp->size += nb;
    return JS_TRUE;
}

static ptrdiff_t
SprintPut(Sprinter *sp, const char *s, size_t len)
{
    ptrdiff_t nb, offset;
    char *bp;

    /* Allocate space for s, including the '\0' at the end. */
    nb = (sp->offset + len + 1) - sp->size;
    if (nb > 0 && !SprintAlloc(sp, nb))
	return -1;

    /* Advance offset and copy s into sp's buffer. */
    offset = sp->offset;
    sp->offset += len;
    bp = sp->base + offset;
    memcpy(bp, s, len);
    bp[len] = 0;
    return offset;
}

static ptrdiff_t
Sprint(Sprinter *sp, const char *format, ...)
{
    va_list ap;
    char *bp;
    ptrdiff_t offset;

    va_start(ap, format);
    bp = JS_vsmprintf(format, ap);	/* XXX vsaprintf */
    va_end(ap);
    if (!bp) {
	JS_ReportOutOfMemory(sp->context);
	return -1;
    }
    offset = SprintPut(sp, bp, strlen(bp));
    free(bp);
    return offset;
}

jschar js_EscapeMap[] = {
    '\b', 'b',
    '\f', 'f',
    '\n', 'n',
    '\r', 'r',
    '\t', 't',
    '\v', 'v',
    '"',  '"',
    '\'', '\''
};

static char *
QuoteString(Sprinter *sp, JSString *str, jschar quote)
{
    ptrdiff_t off, len, nb;
    const jschar *s, *t, *u;
    char *bp;
    jschar c;
    JSBool ok;

    off = sp->offset;
    s = str->chars;
    t = s;
    c = *t;
    if (Sprint(sp, "%c", (char)quote) < 0)
	return NULL;
    do {
	while (JS_ISPRINT(c) && c != quote && !(c >> 8))
	    c = *++t;
	len = PTRDIFF(t, s, jschar);

	/* Allocate space for s, including the '\0' at the end. */
	nb = (sp->offset + len + 1) - sp->size;
	if (nb > 0 && !SprintAlloc(sp, nb))
	    return NULL;

	/* Advance sp->offset and copy s into sp's buffer. */
	bp = sp->base + sp->offset;
	sp->offset += len;
	while (--len >= 0)
	    *bp++ = (char) *s++;
	*bp = '\0';

	if (c == 0)
	    break;
	if ((u = js_strchr(js_EscapeMap, c)) != NULL)
	    ok = Sprint(sp, "\\%c", (char)u[1]) >= 0;
	else
	    ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
	if (!ok)
	    return NULL;
	s = ++t;
	c = *t;
    } while (c != 0);
    if (Sprint(sp, "%c", (char)quote) < 0)
	return NULL;
    return OFF2STR(sp, off);
}

JSString *
js_QuoteString(JSContext *cx, JSString *str, jschar quote)
{
    void *mark;
    Sprinter sprinter;
    char *bytes;
    JSString *escstr;

    mark = JS_ARENA_MARK(&cx->tempPool);
    INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
    bytes = QuoteString(&sprinter, str, quote);
    escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return escstr;
}

/************************************************************************/

struct JSPrinter {
    Sprinter        sprinter;       /* base class state */
    JSArenaPool     pool;           /* string allocation pool */
    uintN           indent;         /* indentation in spaces */
    JSScript        *script;        /* script being printed */
    JSScope         *scope;         /* script function scope */
};

JSPrinter *
js_NewPrinter(JSContext *cx, const char *name, uintN indent)
{
    JSPrinter *jp;
    JSStackFrame *fp;
    JSObject *obj;
    JSObjectMap *map;

    jp = JS_malloc(cx, sizeof(JSPrinter));
    if (!jp)
	return NULL;
    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
    JS_InitArenaPool(&jp->pool, name, 256, 1);
    jp->indent = indent;
    jp->script = NULL;
    jp->scope = NULL;
    fp = cx->fp;
    if (fp && fp->scopeChain) {
	obj = fp->scopeChain;
	map = obj->map;
	if (map->ops == &js_ObjectOps) {
	    if (OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
		obj = fp->fun ? fp->fun->object : NULL;
		if (obj)
		    map = obj->map;
	    }
	    jp->scope = (JSScope *)map;
	}
    }
    return jp;
}

void
js_DestroyPrinter(JSPrinter *jp)
{
    JS_FinishArenaPool(&jp->pool);
    JS_free(jp->sprinter.context, jp);
}

JSString *
js_GetPrinterOutput(JSPrinter *jp)
{
    JSContext *cx;
    JSString *str;

    cx = jp->sprinter.context;
    if (!jp->sprinter.base)
	return cx->runtime->emptyString;
    str = JS_NewStringCopyZ(cx, jp->sprinter.base);
    if (!str)
	return NULL;
    JS_FreeArenaPool(&jp->pool);
    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
    return str;
}

int
js_printf(JSPrinter *jp, char *format, ...)
{
    va_list ap;
    char *bp;
    int cc;

    va_start(ap, format);

    /* Expand magic tab into a run of jp->indent spaces. */
    if (*format == '\t') {
	if (Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
	    return -1;
	format++;
    }

    /* Allocate temp space, convert format, and put. */
    bp = JS_vsmprintf(format, ap);	/* XXX vsaprintf */
    if (!bp) {
	JS_ReportOutOfMemory(jp->sprinter.context);
	return -1;
    }
    cc = strlen(bp);
    if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
	cc = -1;
    free(bp);

    va_end(ap);
    return cc;
}

JSBool
js_puts(JSPrinter *jp, char *s)
{
    return SprintPut(&jp->sprinter, s, strlen(s)) >= 0;
}

/************************************************************************/

typedef struct SprintStack {
    Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
    ptrdiff_t   *offsets;       /* stack of postfix string offsets */
    jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
    uintN       top;            /* top of stack index */
    JSPrinter   *printer;       /* permanent output goes here */
} SprintStack;

/* Gap between stacked strings to allow for insertion of parens and commas. */
#define PAREN_SLOP	(2 + 1)

/*
 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
 * JSOP_SETPROP, and JSOP_SETELEM, respectively.  See the first assertion in
 * PushOff.
 */
#define JSOP_GETPROP2   254
#define JSOP_GETELEM2   255

static JSBool
PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
{
    JS_ASSERT(JSOP_LIMIT <= 254);
    if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))
	return JS_FALSE;
    JS_ASSERT(ss->top < ss->printer->script->depth);
    ss->offsets[ss->top] = off;
    ss->opcodes[ss->top++] = (op >= 128) ? op - 128 : op;
    ss->sprinter.offset += PAREN_SLOP;
    return JS_TRUE;
}

static ptrdiff_t
PopOff(SprintStack *ss, JSOp op)
{
    uintN top;
    JSCodeSpec *cs, *topcs;
    ptrdiff_t off;

    top   = --ss->top;
    cs    = &js_CodeSpec[op];
    topcs = &js_CodeSpec[ss->opcodes[top]];
    if (topcs->prec != 0 && topcs->prec < cs->prec) {
	ss->offsets[top] -= 2;
	ss->sprinter.offset = ss->offsets[top];
	off = Sprint(&ss->sprinter, "(%s)",
		     OFF2STR(&ss->sprinter, ss->sprinter.offset + 2));
    } else {
	off = ss->sprinter.offset = ss->offsets[top];
    }
    return off;
}

#if JS_HAS_SWITCH_STATEMENT
typedef struct TableEntry {
    jsval       key;
    ptrdiff_t   offset;
} TableEntry;

static int
CompareOffsets(const void *v1, const void *v2, void *arg)
{
    const TableEntry *te1 = v1, *te2 = v2;

    return te1->offset - te2->offset;
}

static JSBool
Decompile(SprintStack *ss, jsbytecode *pc, intN nb);

static JSBool
DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
		jsbytecode *pc, ptrdiff_t switchLength,
		ptrdiff_t defaultOffset, JSBool isCondSwitch)
{
    JSContext *cx;
    JSPrinter *jp;
    char *lval, *rval;
    uintN i;
    ptrdiff_t diff, off, off2, caseExprOff;
    jsval key;
    JSString *str;

    cx = ss->sprinter.context;
    jp = ss->printer;

    lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP));
    js_printf(jp, "\tswitch (%s) {\n", lval);

    if (tableLength) {
	diff = table[0].offset - defaultOffset;
	if (diff > 0) {
	    jp->indent += 2;
	    js_printf(jp, "\tdefault:\n");
	    jp->indent += 2;
	    if (!Decompile(ss, pc + defaultOffset, diff))
		return JS_FALSE;
	    jp->indent -= 4;
	}

	caseExprOff = isCondSwitch 
                      ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length
                      : 0;

	for (i = 0; i < tableLength; i++) {
	    off = table[i].offset;
	    if (i + 1 < tableLength)
		off2 = table[i + 1].offset;
	    else
		off2 = switchLength;

	    key = table[i].key;
	    if (isCondSwitch) {
		ptrdiff_t nextCaseExprOff;

		/*
		 * key encodes the JSOP_CASE bytecode's offset from switchtop.
		 * The next case expression follows immediately, unless we are
		 * at the last case.
		 */
		nextCaseExprOff = (ptrdiff_t)
		    (JSVAL_TO_INT(key) + js_CodeSpec[JSOP_CASE].length);
		jp->indent += 2;
		if (!Decompile(ss, pc + caseExprOff,
			       nextCaseExprOff - caseExprOff)) {
		    return JS_FALSE;
		}
		caseExprOff = nextCaseExprOff;
	   } else {
		/*
		 * key comes from an atom, not the decompiler, so we need to
		 * quote it if it's a string literal.
		 */
		str = js_ValueToString(cx, key);
		if (!str)
		    return JS_FALSE;
		jp->indent += 2;
		if (JSVAL_IS_STRING(key)) {
		    rval = QuoteString(&ss->sprinter, str, '"');
		    if (!rval)
			return JS_FALSE;
		} else {
		    rval = JS_GetStringBytes(str);
		}
		js_printf(jp, "\tcase %s:\n", rval);
	    }

	    jp->indent += 2;
	    if (off <= defaultOffset && defaultOffset < off2) {
		diff = defaultOffset - off;
		if (diff != 0) {
		    if (!Decompile(ss, pc + off, diff))
			return JS_FALSE;
		    off = defaultOffset;
		}
		jp->indent -= 2;
		js_printf(jp, "\tdefault:\n");
		jp->indent += 2;
	    }
	    if (!Decompile(ss, pc + off, off2 - off))
		return JS_FALSE;
	    jp->indent -= 4;
	}
    }

    if (defaultOffset == switchLength) {
	jp->indent += 2;
	js_printf(jp, "\tdefault:\n");
	jp->indent -= 2;
    }
    js_printf(jp, "\t}\n");
    return JS_TRUE;
}
#endif

static JSAtom *
GetSlotAtom(JSScope *scope, JSPropertyOp getter, uintN slot)
{
    JSScopeProperty *sprop;

    if (!scope)
	return NULL;
    for (sprop = scope->props; sprop; sprop = sprop->next) {
	if (sprop->getter != getter)
	    continue;
	if ((uintN)JSVAL_TO_INT(sprop->id) == slot)
	    return sym_atom(sprop->symbols);
    }
    return NULL;
}

static JSBool
Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
{
    JSContext *cx;
    JSPrinter *jp;
    jsbytecode *endpc, *done;
    ptrdiff_t len, todo, oplen, cond, next, tail;
    JSOp op, lastop, saveop;
    JSCodeSpec *cs, *topcs;
    jssrcnote *sn;
    const char *lval, *rval = NULL, *xval;
    jsint i, argc;
    char **argv;
    JSAtom *atom;
    JSString *str;
    JSBool ok;
    jsval key;

/*
 * Local macros
*/
#define DECOMPILE_CODE(pc,nb)	if (!Decompile(ss, pc, nb)) return JS_FALSE
#define POP_STR()		OFF2STR(&ss->sprinter, PopOff(ss, op))
#define LOCAL_ASSERT(expr)	JS_ASSERT(expr); if (!(expr)) return JS_FALSE

    cx = ss->sprinter.context;
    jp = ss->printer;
    endpc = pc + nb;
    todo = -2;			/* NB: different from Sprint() error return. */
    op = JSOP_NOP;
    while (pc < endpc) {
	lastop = op;
	op = saveop = *pc;
	if (op >= JSOP_LIMIT) {
	    switch (op) {
	      case JSOP_GETPROP2:
		saveop = JSOP_GETPROP;
		break;
	      case JSOP_GETELEM2:
		saveop = JSOP_GETELEM;
		break;
	      default:;
	    }
	}
	cs = &js_CodeSpec[saveop];
	len = oplen = cs->length;

	if (cs->token) {
	    switch (cs->nuses) {
	      case 2:
		rval = POP_STR();
		lval = POP_STR();
		if ((sn = js_GetSrcNote(jp->script, pc)) != NULL &&
		    SN_TYPE(sn) == SRC_ASSIGNOP) {
		    /* Print only the right operand of the assignment-op. */
		    todo = SprintPut(&ss->sprinter, rval, strlen(rval));
		} else {
		    todo = Sprint(&ss->sprinter, "%s %s %s",
				  lval, cs->token, rval);
		}
		break;

	      case 1:
		rval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval);
		break;

	      case 0:
		todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token));
		break;

	      default:
		todo = -2;
		break;
	    }
	} else {
	    switch (op) {
	      case JSOP_NOP:
		/*
		 * Check for extra user parenthesization, a do-while loop,
		 * a for-loop with an empty initializer part, a labeled
		 * statement, a function definition, or try/finally.
		 */
		sn = js_GetSrcNote(jp->script, pc);
		todo = -2;
		switch (sn ? SN_TYPE(sn) : SRC_NULL) {
		  case SRC_PAREN:
		    /* Use last real op so PopOff adds parens if needed. */
		    todo = PopOff(ss, lastop);

		    /* Now add user-supplied parens only if PopOff did not. */
		    cs    = &js_CodeSpec[lastop];
		    topcs = &js_CodeSpec[ss->opcodes[ss->top]];
		    if (topcs->prec >= cs->prec) {
			todo = Sprint(&ss->sprinter, "(%s)",
				      OFF2STR(&ss->sprinter, todo));
		    }
		    break;

#if JS_HAS_DO_WHILE_LOOP
		  case SRC_WHILE:
		    js_printf(jp, "\tdo {\n");	/* balance} */
		    jp->indent += 4;
		    break;
#endif /* JS_HAS_DO_WHILE_LOOP */

		  case SRC_FOR:
		    rval = "";

		  do_forloop:
		    /* Skip the JSOP_NOP or JSOP_POP bytecode. */
		    pc++;

		    /* Get the cond, next, and loop-closing tail offsets. */
		    cond = js_GetSrcNoteOffset(sn, 0);
		    next = js_GetSrcNoteOffset(sn, 1);
		    tail = js_GetSrcNoteOffset(sn, 2);
		    LOCAL_ASSERT(tail + GET_JUMP_OFFSET(pc + tail) == 0);

		    /* Print the keyword and the possibly empty init-part. */
		    js_printf(jp, "\tfor (%s;", rval);

		    if (pc[cond] == JSOP_IFEQ) {
			/* Decompile the loop condition. */
			DECOMPILE_CODE(pc, cond);
			js_printf(jp, " %s", POP_STR());
		    }

		    /* Need a semicolon whether or not there was a cond. */
		    js_puts(jp, ";");

		    if (pc[next] != JSOP_GOTO) {
			/* Decompile the loop updater. */
			DECOMPILE_CODE(pc + next, tail - next - 1);
			js_printf(jp, " %s", POP_STR());
		    }

		    /* Do the loop body. */
		    js_puts(jp, ") {\n");
		    jp->indent += 4;
		    oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;
		    DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);
		    jp->indent -= 4;
		    js_printf(jp, "\t}\n");

		    /* Set len so pc skips over the entire loop. */
		    len = tail + js_CodeSpec[pc[tail]].length;
		    break;

		  case SRC_LABEL:
		    atom = js_GetAtom(cx, &jp->script->atomMap,
				      (jsatomid) js_GetSrcNoteOffset(sn, 0));
		    jp->indent -= 4;
		    js_printf(jp, "\t%s:\n", ATOM_BYTES(atom));
		    jp->indent += 4;
		    break;

		  case SRC_LABELBRACE:
		    atom = js_GetAtom(cx, &jp->script->atomMap,
				      (jsatomid) js_GetSrcNoteOffset(sn, 0));
		    js_printf(jp, "\t%s: {\n", ATOM_BYTES(atom));
		    jp->indent += 4;
		    break;

		  case SRC_ENDBRACE:
		    jp->indent -= 4;
		    js_printf(jp, "\t}\n");
		    break;

		  case SRC_TRYFIN:
		    if (js_GetSrcNoteOffset(sn, 0) == 0) {
			js_printf(jp, "\ttry {\n");
		    } else {
			jp->indent -= 4;
			js_printf(jp, "\t} finally {\n");
		    }
		    jp->indent += 4;
		    break;

		  case SRC_CATCH:
		    jp->indent -= 4;
		    sn = js_GetSrcNote(jp->script, pc);
		    pc += oplen;
		    js_printf(jp, "\t} catch ("); /* balance) */
		    pc += 6;	/* name Object, pushobj, exception */
		    js_printf(jp, "%s",
			      ATOM_BYTES(GET_ATOM(cx, jp->script, pc)));
		    len = js_GetSrcNoteOffset(sn, 0);
		    pc += 4;	/* initprop, enterwith */
		    if (len) {
			js_printf(jp, " : ");
			DECOMPILE_CODE(pc, len - 3); /* don't decompile ifeq */
			js_printf(jp, "%s", POP_STR());
			pc += len;
		    }
		    js_printf(jp, ") {\n"); /* balance} */
		    jp->indent += 4;
		    len = 0;
		    break;

		  case SRC_FUNCDEF: {
		    JSPrinter *jp2;
		    JSObject *obj;
		    JSFunction *fun;

		    atom = js_GetAtom(cx, &jp->script->atomMap,
				      (jsatomid) js_GetSrcNoteOffset(sn, 0));
		    JS_ASSERT(ATOM_IS_OBJECT(atom));
		    obj = ATOM_TO_OBJECT(atom);
		    fun = JS_GetPrivate(cx, obj);
		    jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),
					jp->indent);
		    if (!jp2)
			return JS_FALSE;
		    jp2->scope = jp->scope;
		    if (js_DecompileFunction(jp2, fun, JS_TRUE)) {
			str = js_GetPrinterOutput(jp2);
			if (str)
			    js_printf(jp, "%s\n", JS_GetStringBytes(str));
		    }
		    js_DestroyPrinter(jp2);
		    break;
		  }
		  default:;
		}
		break;

	      case JSOP_PUSH:
	      case JSOP_PUSHOBJ:
	      case JSOP_BINDNAME:
		todo = Sprint(&ss->sprinter, "");
		break;

#if JS_HAS_EXCEPTIONS
	      case JSOP_GOSUB:
	      case JSOP_RETSUB:
	      case JSOP_SETSP:
		todo = -2;
		break;

	      case JSOP_EXCEPTION:
		sn = js_GetSrcNote(jp->script, pc);
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
		    todo = -2;
		break;
#endif /* JS_HAS_EXCEPTIONS */

	      case JSOP_POP:
	      case JSOP_POPV:
		sn = js_GetSrcNote(jp->script, pc);
		switch (sn ? SN_TYPE(sn) : SRC_NULL) {
		  case SRC_FOR:
		    rval = POP_STR();
		    todo = -2;
		    goto do_forloop;

		  case SRC_PCDELTA:
		    /* Pop and save to avoid blowing stack depth budget. */
		    lval = JS_strdup(cx, POP_STR());
		    if (!lval)
			return JS_FALSE;

		    /* 
                     * The offset tells distance to the end of the right-hand
                     * operand of the comma operator.
                     */
		    done = pc + len;
		    pc += js_GetSrcNoteOffset(sn, 0);
		    len = 0;

		    if (!Decompile(ss, done, pc - done)) {
			JS_free(cx, (char *)lval);
			return JS_FALSE;
		    }

		    /* Pop Decompile result and print comma expression. */
		    rval = POP_STR();
		    todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
		    JS_free(cx, (char *)lval);
		    break;

		  case SRC_HIDDEN:
		    /* Hide this pop, it's from a goto in a with or for/in. */
		    todo = -2;
		    break;

		  default:
		    rval = POP_STR();
		    if (*rval != '\0')
			js_printf(jp, "\t%s;\n", rval);
		    todo = -2;
		    break;
		}
		break;

	      case JSOP_POP2:
		(void) PopOff(ss, op);
		(void) PopOff(ss, op);
		todo = -2;
		break;

	      case JSOP_ENTERWITH:
		sn = js_GetSrcNote(jp->script, pc);
		todo = -2;
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
		    break;
		rval = POP_STR();
		js_printf(jp, "\twith (%s) {\n", rval);
		jp->indent += 4;
		break;

	      case JSOP_LEAVEWITH:
		sn = js_GetSrcNote(jp->script, pc);
		todo = -2;
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
		    break;
		jp->indent -= 4;
		js_printf(jp, "\t}\n");
		break;

	      case JSOP_RETURN:
		rval = POP_STR();
		if (*rval != '\0')
		    js_printf(jp, "\t%s %s;\n", cs->name, rval);
		else
		    js_printf(jp, "\t%s;\n", cs->name);
		todo = -2;
		break;

#if JS_HAS_EXCEPTIONS
	      case JSOP_THROW:
		sn = js_GetSrcNote(jp->script, pc);
		todo = -2;
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
		    break;
		rval = POP_STR();
		js_printf(jp, "\t%s %s;\n", cs->name, rval);
		break;
#endif /* JS_HAS_EXCEPTIONS */

	      case JSOP_GOTO:
		sn = js_GetSrcNote(jp->script, pc);
		switch (sn ? SN_TYPE(sn) : SRC_NULL) {
		  case SRC_CONT2LABEL:
		    atom = js_GetAtom(cx, &jp->script->atomMap,
				      (jsatomid) js_GetSrcNoteOffset(sn, 0));
		    js_printf(jp, "\tcontinue %s;\n", ATOM_BYTES(atom));
		    break;
		  case SRC_CONTINUE:
		    js_printf(jp, "\tcontinue;\n");
		    break;
		  case SRC_BREAK2LABEL:
		    atom = js_GetAtom(cx, &jp->script->atomMap,
				      (jsatomid) js_GetSrcNoteOffset(sn, 0));
		    js_printf(jp, "\tbreak %s;\n", ATOM_BYTES(atom));
		    break;
		  case SRC_HIDDEN:
		    break;
		  default:
		    js_printf(jp, "\tbreak;\n");
		    break;
		}
		todo = -2;
		break;

	      case JSOP_IFEQ:
		len = GET_JUMP_OFFSET(pc);
		sn = js_GetSrcNote(jp->script, pc);

		switch (sn ? SN_TYPE(sn) : SRC_NULL) {
		  case SRC_IF:
		  case SRC_IF_ELSE:
		    rval = POP_STR();
		    js_printf(jp, "\tif (%s) {\n", rval);
		    jp->indent += 4;
		    if (SN_TYPE(sn) == SRC_IF) {
			DECOMPILE_CODE(pc + oplen, len - oplen);
		    } else {
			DECOMPILE_CODE(pc + oplen,
			    len - (oplen + js_CodeSpec[JSOP_GOTO].length));
			jp->indent -= 4;
			pc += len - oplen;
			LOCAL_ASSERT(*pc == JSOP_GOTO);
			oplen = js_CodeSpec[JSOP_GOTO].length;
			len = GET_JUMP_OFFSET(pc);
			js_printf(jp, "\t} else {\n");
			jp->indent += 4;
			DECOMPILE_CODE(pc + oplen, len - oplen);
		    }
		    jp->indent -= 4;
		    js_printf(jp, "\t}\n");
		    todo = -2;
		    break;

		  case SRC_WHILE:
		    rval = POP_STR();
		    js_printf(jp, "\twhile (%s) {\n", rval);
		    jp->indent += 4;
		    DECOMPILE_CODE(pc + oplen,
			len - (oplen + js_CodeSpec[JSOP_GOTO].length));
		    jp->indent -= 4;
		    js_printf(jp, "\t}\n");
		    todo = -2;
		    break;

		  case SRC_COND:
		    xval = JS_strdup(cx, POP_STR());
		    if (!xval)
			return JS_FALSE;
		    DECOMPILE_CODE(pc + oplen,
			len - (oplen + js_CodeSpec[JSOP_GOTO].length));
		    lval = JS_strdup(cx, POP_STR());
		    if (!lval) {
			JS_free(cx, (void *)xval);
			return JS_FALSE;
		    }
		    pc += len - oplen;
		    LOCAL_ASSERT(*pc == JSOP_GOTO);
		    oplen = js_CodeSpec[JSOP_GOTO].length;
		    len = GET_JUMP_OFFSET(pc);
		    DECOMPILE_CODE(pc + oplen, len - oplen);
		    rval = POP_STR();
		    todo = Sprint(&ss->sprinter, "%s ? %s : %s",
				  xval, lval, rval);
		    JS_free(cx, (void *)xval);
		    JS_free(cx, (void *)lval);
		    break;

		  default:
#if JS_BUG_SHORT_CIRCUIT
		    {
			/* top is the first clause in a disjunction (||). */
			jsbytecode *ifeq;

			lval = JS_strdup(cx, POP_STR());
			if (!lval)
			    return JS_FALSE;
			ifeq = pc + len;
			LOCAL_ASSERT(pc[oplen] == JSOP_TRUE);
			pc += oplen + js_CodeSpec[JSOP_TRUE].length;
			LOCAL_ASSERT(*pc == JSOP_GOTO);
			oplen = js_CodeSpec[JSOP_GOTO].length;
			done = pc + GET_JUMP_OFFSET(pc);
			pc += oplen;
			DECOMPILE_CODE(pc, done - ifeq);
			rval = POP_STR();
			todo = Sprint(&ss->sprinter, "%s || %s", lval, rval);
			JS_free(cx, (void *)lval);
			len = PTRDIFF(done, pc, jsbytecode);
		    }
#endif /* JS_BUG_SHORT_CIRCUIT */
		    break;
		}
		break;

	      case JSOP_IFNE:
#if JS_HAS_DO_WHILE_LOOP
		/* Check for a do-while loop's upward branch. */
		sn = js_GetSrcNote(jp->script, pc);
		if (sn && SN_TYPE(sn) == SRC_WHILE) {
		    jp->indent -= 4;
		    /* {balance: */
		    js_printf(jp, "\t} while (%s);\n", POP_STR());
		    todo = -2;
		    break;
		}
#endif /* JS_HAS_DO_WHILE_LOOP */

#if JS_BUG_SHORT_CIRCUIT
		{
		    jsbytecode *ifne;

		    /* This bytecode is used only for conjunction (&&). */
		    lval = JS_strdup(cx, POP_STR());
		    if (!lval)
			return JS_FALSE;
		    ifne = pc + GET_JUMP_OFFSET(pc);
		    LOCAL_ASSERT(pc[oplen] == JSOP_FALSE);
		    pc += len + js_CodeSpec[JSOP_FALSE].length;
		    LOCAL_ASSERT(*pc == JSOP_GOTO);
		    oplen = js_CodeSpec[JSOP_GOTO].length;
		    done = pc + GET_JUMP_OFFSET(pc);
		    pc += oplen;
		    DECOMPILE_CODE(pc, done - ifne);
		    rval = POP_STR();
		    todo = Sprint(&ss->sprinter, "%s && %s", lval, rval);
		    JS_free(cx, (void *)lval);
		    len = PTRDIFF(done, pc, jsbytecode);
		}
#endif /* JS_BUG_SHORT_CIRCUIT */
		break;

#if !JS_BUG_SHORT_CIRCUIT
	      case JSOP_OR:
		/* Top of stack is the first clause in a disjunction (||). */
		lval = JS_strdup(cx, POP_STR());
		if (!lval)
		    return JS_FALSE;
		done = pc + GET_JUMP_OFFSET(pc);
		pc += len;
		len = PTRDIFF(done, pc, jsbytecode);
		DECOMPILE_CODE(pc, len);
		rval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s || %s", lval, rval);
		JS_free(cx, (char *)lval);
		break;

	      case JSOP_AND:
		/* Top of stack is the first clause in a conjunction (&&). */
		lval = JS_strdup(cx, POP_STR());
		if (!lval)
		    return JS_FALSE;
		done = pc + GET_JUMP_OFFSET(pc);
		pc += len;
		len = PTRDIFF(done, pc, jsbytecode);
		DECOMPILE_CODE(pc, len);
		rval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s && %s", lval, rval);
		JS_free(cx, (char *)lval);
		break;
#endif

	      case JSOP_FORNAME:
		rval = POP_STR();
		/* FALL THROUGH */
	      case JSOP_FORNAME2:
		xval = NULL;
		atom = NULL;
		lval = ATOM_BYTES(GET_ATOM(cx, jp->script, pc));
		sn = js_GetSrcNote(jp->script, pc);
		pc += oplen;
		goto do_forinloop;

	      case JSOP_FORPROP:
		rval = POP_STR();
		/* FALL THROUGH */
	      case JSOP_FORPROP2:
		xval = NULL;
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		sn = NULL;
		pc += oplen;
		goto do_forinloop;

	      case JSOP_FORELEM:
		rval = POP_STR();
		/* FALL THROUGH */
	      case JSOP_FORELEM2:
		xval = POP_STR();
		atom = NULL;
		lval = POP_STR();
		sn = NULL;
		pc++;

	      do_forinloop:
		LOCAL_ASSERT(*pc == JSOP_IFEQ);
		oplen = js_CodeSpec[JSOP_IFEQ].length;
		len = GET_JUMP_OFFSET(pc);
		js_printf(jp, "\tfor (%s%s",
			  (sn && SN_TYPE(sn) == SRC_VAR) ? "var " : "", lval);
		if (atom)
		    js_printf(jp, ".%s", ATOM_BYTES(atom));
		else if (xval)
		    js_printf(jp, "[%s]", xval);
		if (cs->format & JOF_FOR2)
		    rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
		js_printf(jp, " in %s) {\n", rval);
		jp->indent += 4;
		DECOMPILE_CODE(pc + oplen,
		    len - (oplen + js_CodeSpec[JSOP_GOTO].length));
		jp->indent -= 4;
		js_printf(jp, "\t}\n");
		todo = -2;
		break;

	      case JSOP_DUP2:
		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
		todo = SprintPut(&ss->sprinter, rval, strlen(rval));
		if (todo < 0 || !PushOff(ss, todo, op))
		    return JS_FALSE;
		/* FALL THROUGH */

	      case JSOP_DUP:
		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
		todo = SprintPut(&ss->sprinter, rval, strlen(rval));
		break;

	      case JSOP_SETARG:
		atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc));
		LOCAL_ASSERT(atom);
		goto do_setname;

	      case JSOP_SETVAR:
		atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc));
		LOCAL_ASSERT(atom);
		goto do_setname;

	      case JSOP_SETNAME:
	      case JSOP_SETNAME2:
		atom = GET_ATOM(cx, jp->script, pc);
	      do_setname:
		lval = ATOM_BYTES(atom);
		rval = POP_STR();
		if (op == JSOP_SETNAME2)
		    (void) PopOff(ss, op);
		if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL &&
		    SN_TYPE(sn) == SRC_ASSIGNOP) {
		    todo = Sprint(&ss->sprinter, "%s %s= %s",
				  lval, js_CodeSpec[pc[-1]].token, rval);
		} else {
		    sn = js_GetSrcNote(jp->script, pc);
		    todo = Sprint(&ss->sprinter, "%s%s = %s",
				  (sn && SN_TYPE(sn) == SRC_VAR) ? "var " : "",
				  lval, rval);
		}
		break;

	      case JSOP_NEW:
	      case JSOP_CALL:
	      case JSOP_CALLSPECIAL:
		saveop = op;
		op = JSOP_NOP;           /* turn off parens */
		argc = GET_ARGC(pc);
		argv = JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
		if (!argv)
		    return JS_FALSE;

		ok = JS_TRUE;
		for (i = argc; i > 0; i--) {
		    argv[i] = JS_strdup(cx, POP_STR());
		    if (!argv[i]) {
			ok = JS_FALSE;
			break;
		    }
		}

		/* Skip the JSOP_PUSHOBJ-created empty string. */
		LOCAL_ASSERT(ss->top >= 2);
		(void) PopOff(ss, op);

		/* Get the callee's decompiled image in argv[0]. */
		argv[0] = JS_strdup(cx, POP_STR());
		if (!argv[i])
		    ok = JS_FALSE;

		lval = "(", rval = ")";
		if (saveop == JSOP_NEW) {
		    todo = Sprint(&ss->sprinter, "%s %s%s",
				  js_new_str, argv[0], lval);
		} else {
		    todo = Sprint(&ss->sprinter, "%s%s",
				  argv[0], lval);
		}
		if (todo < 0)
		    ok = JS_FALSE;

		for (i = 1; i <= argc; i++) {
		    if (!argv[i] ||
			Sprint(&ss->sprinter, "%s%s",
			       argv[i], (i < argc) ? ", " : "") < 0) {
			ok = JS_FALSE;
			break;
		    }
		}
		if (Sprint(&ss->sprinter, rval) < 0)
		    ok = JS_FALSE;

		for (i = 0; i <= argc; i++) {
		    if (argv[i])
			JS_free(cx, argv[i]);
		}
		JS_free(cx, argv);
		if (!ok)
		    return JS_FALSE;
		op = saveop;
		break;

	      case JSOP_DELNAME:
		atom = GET_ATOM(cx, jp->script, pc);
		lval = ATOM_BYTES(atom);
		todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
		break;

	      case JSOP_DELPROP:
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s %s.%s",
			      js_delete_str, lval, ATOM_BYTES(atom));
		break;

	      case JSOP_DELELEM:
		xval = POP_STR();
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s %s[%s]",
			      js_delete_str, lval, xval);
		break;

	      case JSOP_TYPEOF:
	      case JSOP_VOID:
		rval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);
		break;

	      case JSOP_INCARG:
	      case JSOP_DECARG:
		atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc));
		LOCAL_ASSERT(atom);
		goto do_incatom;

	      case JSOP_INCVAR:
	      case JSOP_DECVAR:
		atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc));
		LOCAL_ASSERT(atom);
		goto do_incatom;

	      case JSOP_INCNAME:
	      case JSOP_DECNAME:
		atom = GET_ATOM(cx, jp->script, pc);
	      do_incatom:
		lval = ATOM_BYTES(atom);
		todo = Sprint(&ss->sprinter, "%s%s",
			      js_incop_str[!(cs->format & JOF_INC)], lval);
		break;

	      case JSOP_INCPROP:
	      case JSOP_DECPROP:
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s%s.%s",
			      js_incop_str[!(cs->format & JOF_INC)],
			      lval, ATOM_BYTES(atom));
		break;

	      case JSOP_INCELEM:
	      case JSOP_DECELEM:
		xval = POP_STR();
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s%s[%s]",
			      js_incop_str[!(cs->format & JOF_INC)],
			      lval, xval);
		break;

	      case JSOP_ARGINC:
	      case JSOP_ARGDEC:
		atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc));
		LOCAL_ASSERT(atom);
		goto do_atominc;

	      case JSOP_VARINC:
	      case JSOP_VARDEC:
		atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc));
		LOCAL_ASSERT(atom);
		goto do_atominc;

	      case JSOP_NAMEINC:
	      case JSOP_NAMEDEC:
		atom = GET_ATOM(cx, jp->script, pc);
	      do_atominc:
		lval = ATOM_BYTES(atom);
		todo = Sprint(&ss->sprinter, "%s%s",
			      lval, js_incop_str[!(cs->format & JOF_INC)]);
		break;

	      case JSOP_PROPINC:
	      case JSOP_PROPDEC:
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s.%s%s",
			      lval, ATOM_BYTES(atom),
			      js_incop_str[!(cs->format & JOF_INC)]);
		break;

	      case JSOP_ELEMINC:
	      case JSOP_ELEMDEC:
		xval = POP_STR();
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s[%s]%s",
			      lval, xval,
			      js_incop_str[!(cs->format & JOF_INC)]);
		break;

	      case JSOP_GETPROP2:
		op = JSOP_GETPROP;
		(void) PopOff(ss, lastop);
		/* FALL THROUGH */

	      case JSOP_GETPROP:
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s.%s", lval, ATOM_BYTES(atom));
		break;

	      case JSOP_SETPROP:
		rval = POP_STR();
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL &&
		    SN_TYPE(sn) == SRC_ASSIGNOP) {
		    todo = Sprint(&ss->sprinter, "%s.%s %s= %s",
				  lval, ATOM_BYTES(atom),
				  js_CodeSpec[pc[-1]].token, rval);
		} else {
		    todo = Sprint(&ss->sprinter, "%s.%s = %s",
				  lval, ATOM_BYTES(atom), rval);
		}
		break;

	      case JSOP_GETELEM2:
		op = JSOP_GETELEM;
		(void) PopOff(ss, lastop);
		/* FALL THROUGH */

	      case JSOP_GETELEM:
		op = JSOP_NOP;           /* turn off parens */
		xval = POP_STR();
		op = JSOP_GETELEM;
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval);
		break;

	      case JSOP_SETELEM:
		op = JSOP_NOP;           /* turn off parens */
		rval = POP_STR();
		xval = POP_STR();
		op = JSOP_SETELEM;
		lval = POP_STR();
		if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL &&
		    SN_TYPE(sn) == SRC_ASSIGNOP) {
		    todo = Sprint(&ss->sprinter, "%s[%s] %s= %s",
				  lval, xval,
				  js_CodeSpec[pc[-1]].token, rval);
		} else {
		    todo = Sprint(&ss->sprinter, "%s[%s] = %s",
				  lval, xval, rval);
		}
		break;

	      case JSOP_GETARG:
		atom = GetSlotAtom(jp->scope, js_GetArgument, GET_ARGNO(pc));
		LOCAL_ASSERT(atom);
		goto do_name;

	      case JSOP_GETVAR:
		atom = GetSlotAtom(jp->scope, js_GetLocalVariable, GET_VARNO(pc));
		LOCAL_ASSERT(atom);
		goto do_name;

	      case JSOP_NAME:
		atom = GET_ATOM(cx, jp->script, pc);
	      do_name:
		sn = js_GetSrcNote(jp->script, pc);
		todo = Sprint(&ss->sprinter,
			      (sn && SN_TYPE(sn) == SRC_VAR) ? "var %s" : "%s",
			      ATOM_BYTES(atom));
		break;

	      case JSOP_UINT16:
		i = (jsint) GET_ATOM_INDEX(pc);
		todo = Sprint(&ss->sprinter, "%u", (unsigned) i);
		break;

	      case JSOP_NUMBER:
	      case JSOP_STRING:
	      case JSOP_OBJECT:
		atom = GET_ATOM(cx, jp->script, pc);
		key = ATOM_KEY(atom);
		if (JSVAL_IS_INT(key)) {
		    long ival = (long)JSVAL_TO_INT(key);
		    todo = Sprint(&ss->sprinter, "%ld", ival);
		} else if (JSVAL_IS_DOUBLE(key)) {
		    char buf[32];
		    JS_cnvtf(buf, sizeof buf, 20, *JSVAL_TO_DOUBLE(key));
		    todo = Sprint(&ss->sprinter, buf);
		} else if (JSVAL_IS_STRING(key)) {
		    rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
				       '"');
		    if (!rval)
			return JS_FALSE;
		    todo = Sprint(&ss->sprinter, "%s", rval);
		} else if (JSVAL_IS_OBJECT(key)) {
#if JS_HAS_LEXICAL_CLOSURE
		    if (JSVAL_IS_FUNCTION(cx, key))
			goto do_closure;
#endif
		    str = js_ValueToString(cx, key);
		    if (!str)
			return JS_FALSE;
		    todo = SprintPut(&ss->sprinter,
				     JS_GetStringBytes(str),
				     str->length);
		} else {
		    todo = -2;
		}
		break;

#if JS_HAS_SWITCH_STATEMENT
	      case JSOP_TABLESWITCH:
	      {
		jsbytecode *pc2, *end;
		ptrdiff_t off, off2;
		jsint j, n, low, high;
		TableEntry *table;

		sn = js_GetSrcNote(jp->script, pc);
		JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
		len = js_GetSrcNoteOffset(sn, 0);
		pc2 = pc;
		off = GET_JUMP_OFFSET(pc2);
		end = pc + off;
		pc2 += JUMP_OFFSET_LEN;
		low = GET_JUMP_OFFSET(pc2);
		pc2 += JUMP_OFFSET_LEN;
		high = GET_JUMP_OFFSET(pc2);

		n = high - low + 1;
		table = JS_malloc(cx, (size_t)n * sizeof *table);
		if (!table)
		    return JS_FALSE;
		if (pc2 + JUMP_OFFSET_LEN + 1 >= end) {
		    j = 0;
		} else {
		    for (i = j = 0; i < n; i++) {
			pc2 += JUMP_OFFSET_LEN;
			off2 = GET_JUMP_OFFSET(pc2);
			if (off2) {
			    table[j].key = INT_TO_JSVAL(low + i);
			    table[j++].offset = off2;
			}
		    }
		}
		js_qsort(table, (size_t)j, sizeof *table, CompareOffsets, NULL);

		ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
				     JS_FALSE);
		JS_free(cx, table);
		if (!ok)
		    return ok;
		todo = -2;
		break;
	      }

	      case JSOP_LOOKUPSWITCH:
	      {
		jsbytecode *pc2;
		ptrdiff_t off, off2;
		jsint npairs;
		TableEntry *table;

		sn = js_GetSrcNote(jp->script, pc);
		JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
		len = js_GetSrcNoteOffset(sn, 0);
		pc2 = pc;
		off = GET_JUMP_OFFSET(pc2);
		pc2 += JUMP_OFFSET_LEN;
		npairs = (jsint) GET_ATOM_INDEX(pc2);
		pc2 += ATOM_INDEX_LEN;

		table = JS_malloc(cx, (size_t)npairs * sizeof *table);
		if (!table)
		    return JS_FALSE;
		for (i = 0; i < npairs; i++) {
		    atom = GET_ATOM(cx, jp->script, pc2);
		    pc2 += ATOM_INDEX_LEN;
		    off2 = GET_JUMP_OFFSET(pc2);
		    pc2 += JUMP_OFFSET_LEN;
		    table[i].key = ATOM_KEY(atom);
		    table[i].offset = off2;
		}

		ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
				     JS_FALSE);
		JS_free(cx, table);
		if (!ok)
		    return ok;
		todo = -2;
		break;
	      }

	      case JSOP_CONDSWITCH:
	      {
		jsbytecode *pc2;
		ptrdiff_t off, off2, caseOff;
		jsint ncases;
		TableEntry *table;

		sn = js_GetSrcNote(jp->script, pc);
		JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
		len = js_GetSrcNoteOffset(sn, 0);
		off = js_GetSrcNoteOffset(sn, 1);

		/*
		 * Count the cases using offsets from switch to first case,
		 * and case to case, stored in srcnote immediates.
		 */
		pc2 = pc;
		off2 = off;
		for (ncases = 0; off2 != 0; ncases++) {
		    pc2 += off2;
		    JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);
		    if (*pc2 == JSOP_DEFAULT) {
			/* End of cases, but count default as a case. */
			off2 = 0;
		    } else {
			sn = js_GetSrcNote(jp->script, pc2);
			JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
			off2 = js_GetSrcNoteOffset(sn, 0);
		    }
		}

		/*
		 * Allocate table and rescan the cases using their srcnotes,
		 * stashing each case's delta from switch top in table[i].key,
		 * and the distance to its statements in table[i].offset.
		 */
		table = JS_malloc(cx, (size_t)ncases * sizeof *table);
		if (!table)
		    return JS_FALSE;
		pc2 = pc;
		off2 = off;
		for (i = 0; i < ncases; i++) {
		    pc2 += off2;
		    JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);
		    caseOff = pc2 - pc;
		    table[i].key = INT_TO_JSVAL((jsint) caseOff);
		    table[i].offset = caseOff + GET_JUMP_OFFSET(pc2);
		    if (*pc2 == JSOP_CASE) {
			sn = js_GetSrcNote(jp->script, pc2);
			JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
			off2 = js_GetSrcNoteOffset(sn, 0);
		    }
		}

		/*
		 * Find offset of default code by fetching the default offset
		 * from the end of table.  JSOP_CONDSWITCH always has a default
		 * case at the end.
		 */
		off = JSVAL_TO_INT(table[ncases-1].key);
		pc2 = pc + off;
		off += GET_JUMP_OFFSET(pc2);

		ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
				     JS_TRUE);
		JS_free(cx, table);
		if (!ok)
		    return ok;
		todo = -2;
		break;
	      }

	      case JSOP_CASE:
	      {
		lval = POP_STR();
		if (!lval)
		    return JS_FALSE;
		js_printf(jp, "\tcase %s:\n", lval);
		todo = -2;
		break;
	      }

#endif /* JS_HAS_SWITCH_STATEMENT */

#if !JS_BUG_FALLIBLE_EQOPS
	      case JSOP_NEW_EQ:
	      case JSOP_NEW_NE:
		rval = POP_STR();
		lval = POP_STR();
		todo = Sprint(&ss->sprinter, "%s %c%s %s",
			      lval,
			      (op == JSOP_NEW_EQ) ? '=' : '!',
#if JS_HAS_TRIPLE_EQOPS
			      JSVERSION_IS_ECMA(cx->version) ? "==" :
#endif
			      "=",
			      rval);
		break;
#endif /* !JS_BUG_FALLIBLE_EQOPS */

#if JS_HAS_LEXICAL_CLOSURE
	      case JSOP_CLOSURE:
	      {
		JSObject *obj;
		JSFunction *fun;
		JSPrinter *jp2;

		atom = GET_ATOM(cx, jp->script, pc);
		JS_ASSERT(ATOM_IS_OBJECT(atom));
	      do_closure:
		obj = ATOM_TO_OBJECT(atom);
		fun = JS_GetPrivate(cx, obj);
		jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), jp->indent);
		if (!jp2)
		    return JS_FALSE;
		jp2->scope = jp->scope;
		todo = -1;
		if (js_DecompileFunction(jp2, fun, JS_FALSE)) {
		    str = js_GetPrinterOutput(jp2);
		    if (str) {
			todo = SprintPut(&ss->sprinter,
					 JS_GetStringBytes(str),
					 str->length);
		    }
		}
		js_DestroyPrinter(jp2);
		break;
	      }
#endif /* JS_HAS_LEXICAL_CLOSURE */

#if JS_HAS_EXPORT_IMPORT
	      case JSOP_EXPORTALL:
		js_printf(jp, "\texport *\n");
		todo = -2;
		break;

	      case JSOP_EXPORTNAME:
		atom = GET_ATOM(cx, jp->script, pc);
		js_printf(jp, "\texport %s\n", ATOM_BYTES(atom));
		todo = -2;
		break;

	      case JSOP_IMPORTALL:
		lval = POP_STR();
		js_printf(jp, "\timport %s.*\n", lval);
		todo = -2;
		break;

	      case JSOP_IMPORTPROP:
		atom = GET_ATOM(cx, jp->script, pc);
		lval = POP_STR();
		js_printf(jp, "\timport %s.%s\n", lval, ATOM_BYTES(atom));
		todo = -2;
		break;

	      case JSOP_IMPORTELEM:
		xval = POP_STR();
		op = JSOP_GETELEM;
		lval = POP_STR();
		js_printf(jp, "\timport %s[%s]\n", lval, xval);
		todo = -2;
		break;
#endif /* JS_HAS_EXPORT_IMPORT */

	      case JSOP_TRAP:
		op = JS_GetTrapOpcode(cx, jp->script, pc);
		if (op == JSOP_LIMIT)
		    return JS_FALSE;
		*pc = op;
		cs = &js_CodeSpec[op];
		len = cs->length;
		DECOMPILE_CODE(pc, len);
		*pc = JSOP_TRAP;
		todo = -2;
		break;

#if JS_HAS_INITIALIZERS
	      case JSOP_NEWINIT:
		LOCAL_ASSERT(ss->top >= 2);
		(void) PopOff(ss, op);
		lval = POP_STR();
#if JS_HAS_SHARP_VARS
		op = pc[len];
		if (op == JSOP_DEFSHARP) {
		    pc += len;
		    cs = &js_CodeSpec[op];
		    len = cs->length;
		    i = (jsint) GET_ATOM_INDEX(pc);
		    todo = Sprint(&ss->sprinter, "#%u=%c",
				  (unsigned) i,
				  (*lval == 'O') ? '{' : '[');
		    /* balance}] */
		} else
#endif /* JS_HAS_SHARP_VARS */
		{
		    todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
		    /* balance}] */
		}
		break;

	      case JSOP_ENDINIT:
		rval = POP_STR();
		sn = js_GetSrcNote(jp->script, pc);
		todo = Sprint(&ss->sprinter, "%s%s%c",
			      rval,
			      (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
			      /* [balance */
			      (*rval == '{') ? '}' : ']');
		break;

	      case JSOP_INITPROP:
		rval = POP_STR();
		atom = GET_ATOM(cx, jp->script, pc);
		xval = ATOM_BYTES(atom);
		lval = POP_STR();
	      do_initprop:
		todo = Sprint(&ss->sprinter, "%s%s%s:%s",
			      lval, (lval[1] != '\0') ? ", " : "",
			      xval, rval);
		break;

	      case JSOP_INITELEM:
		rval = POP_STR();
		xval = POP_STR();
		lval = POP_STR();
		sn = js_GetSrcNote(jp->script, pc);
		if (sn && SN_TYPE(sn) == SRC_LABEL)
		    goto do_initprop;
		todo = Sprint(&ss->sprinter, "%s%s%s",
			      lval,
			      (lval[1] != '\0' || *xval != '0') ? ", " : "",
			      rval);
		break;

#if JS_HAS_SHARP_VARS
	      case JSOP_DEFSHARP:
		i = (jsint) GET_ATOM_INDEX(pc);
		rval = POP_STR();
		todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
		break;

	      case JSOP_USESHARP:
		i = (jsint) GET_ATOM_INDEX(pc);
		todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
		break;
#endif /* JS_HAS_SHARP_VARS */
#endif /* JS_HAS_INITIALIZERS */

#if JS_HAS_DEBUGGER_KEYWORD
	      case JSOP_DEBUGGER:
		js_printf(jp, "\tdebugger;\n");
		todo = -2;
		break;
#endif /* JS_HAS_DEBUGGER_KEYWORD */

	      default:
		todo = -2;
		break;
	    }
	}

	if (todo < 0) {
	    /* -2 means "don't push", -1 means reported error. */
	    if (todo == -1)
		return JS_FALSE;
	} else {
	    if (!PushOff(ss, todo, op))
		return JS_FALSE;
	}
	pc += len;
    }

/*
 * Undefine local macros.
 */
#undef DECOMPILE_CODE
#undef POP_STR
#undef LOCAL_ASSERT

    return JS_TRUE;
}


JSBool
js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len)
{
    SprintStack ss;
    JSContext *cx;
    void *mark;
    JSScript *oldscript;
    JSBool ok;
    char *last;

    /* Initialize a sprinter for use with the offset stack. */
    ss.printer = jp;
    cx = jp->sprinter.context;
    mark = JS_ARENA_MARK(&cx->tempPool);
    INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP);

    /* Initialize the offset and opcode stacks. */
    ss.offsets = JS_malloc(cx, script->depth * sizeof *ss.offsets);
    ss.opcodes = JS_malloc(cx, script->depth * sizeof *ss.opcodes);
    if (!ss.offsets || !ss.opcodes) {
	if (ss.offsets)
	    JS_free(cx, ss.offsets);
	return JS_FALSE;
    }
    ss.top = 0;

    /* Call recursive subroutine to do the hard work. */
    oldscript = jp->script;
    jp->script = script;
    ok = Decompile(&ss, pc, len);
    jp->script = oldscript;

    /* If the given code didn't empty the stack, do it now. */
    if (ss.top) {
	do {
	    last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP));
	} while (ss.top);
	js_printf(jp, "%s", last);
    }

    /* Free and clear temporary stuff. */
    JS_free(cx, ss.offsets);
    JS_free(cx, ss.opcodes);
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return ok;
}

JSBool
js_DecompileScript(JSPrinter *jp, JSScript *script)
{
    return js_DecompileCode(jp, script, script->code, (uintN)script->length);
}

JSBool
js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun, JSBool newlines)
{
    JSScript *script = fun->script;
    if (script) {
        JSScope *oldScope, *scope = NULL;
        JSBool ok;
        if (fun->object) scope = (JSScope *)fun->object->map;
        oldScope = jp->scope;
        jp->scope = scope;
        ok = js_DecompileCode(jp, script, script->code, (uintN)script->length);
        jp->scope = oldScope;
        return ok;
    }
    else {
	js_printf(jp, "\t[native code]\n");
        return JS_TRUE;
    }
}

JSBool
js_DecompileFunction(JSPrinter *jp, JSFunction *fun, JSBool newlines)
{
    JSScope *scope, *oldscope;
    JSScopeProperty *sprop, *snext;
    JSBool ok;
    JSAtom *atom;
    uintN indent;
    intN i;

    if (newlines) {
	js_puts(jp, "\n");
	js_printf(jp, "\t");
    }
    js_printf(jp, "function %s(", fun->atom ? ATOM_BYTES(fun->atom) : "");

    scope = NULL;
    if (fun->script && fun->object) {
	/* Print the parameters.
	 *
	 * This code is complicated by the need to handle duplicate parameter names.
	 * A duplicate parameter is stored as a property with id equal to the parameter
	 * number, but will not be in order in the linked list of symbols. So for each
	 * parameter we search the list of symbols for the appropriately numbered
	 * parameter, which we can then print.
	 */
	for (i=0;;i++) {
	    jsid id;
	    atom = NULL;
	    scope = (JSScope *)fun->object->map;
	    for (sprop = scope->props; sprop; sprop = snext) {
		snext = sprop->next;
		if (sprop->getter != js_GetArgument)
		    continue;
		if (JSVAL_IS_INT(sprop->id) && JSVAL_TO_INT(sprop->id) == i) {
		    atom = sym_atom(sprop->symbols);
		    break;
		}
		id = (jsid) sym_atom(sprop->symbols);
		if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) == i) {
		    atom = (JSAtom *) sprop->id;
		    break;
		}
	    }
	    if (atom == NULL)
		break;
	    js_printf(jp, (i > 0 ? ", %s" : "%s"), ATOM_BYTES(atom));
	}
    }
    js_puts(jp, ") {\n");
    indent = jp->indent;
    jp->indent += 4;
    if (fun->script && fun->object) {
	oldscope = jp->scope;
	jp->scope = scope;
	ok = js_DecompileScript(jp, fun->script);
	jp->scope = oldscope;
	if (!ok) {
	    jp->indent = indent;
	    return JS_FALSE;
	}
    } else {
	js_printf(jp, "\t[native code]\n");
    }
    jp->indent -= 4;
    js_printf(jp, "\t}");
    if (newlines)
	js_puts(jp, "\n");
    return JS_TRUE;
}

JSString *
js_DecompileValueGenerator(JSContext *cx, jsval v, JSString *fallback)
{
    JSStackFrame *fp;
    jsbytecode *pc, *begin, *end, *tmp;
    jsval *sp, *base, *limit;
    JSScript *script;
    JSCodeSpec *cs;
    uint32 format, mode;
    intN depth;
    jssrcnote *sn;
    uintN len, maxoplen;
    JSPrinter *jp;
    JSString *name;

    fp = cx->fp;
    if (!fp)
	goto do_fallback;

    /* Try to find sp's generating pc depth slots under it on the stack. */
    pc = fp->pc;
    limit = (jsval *) cx->stackPool.current->avail;
    if (!pc &&
	fp->argv &&
	fp->down &&
	(script = fp->down->script) != NULL) {
	/*
	 * Native frame called by script: try to match v with actual argument.
	 * If (fp->sp < fp->argv), normally an impossibility, then we are in
	 * js_ReportIsNotFunction and sp points to the offending non-function
	 * on the stack.
	 */
	for (sp = (fp->sp < fp->argv) ? fp->sp : fp->argv; sp < limit; sp++) {
	    if (*sp == v) {
		depth = (intN)script->depth;
		pc = (jsbytecode *) sp[-depth];
		break;
	    }
	}
    } else {
	/* Could be native frame called by native code, so check script. */
	script = fp->script;
	if (!script)
	    goto do_fallback;

	/* OK, interpreted frame.  Try the operand at sp, or one above it. */
	sp = fp->sp;
	if (sp[0] != v && sp + 1 < limit && sp[1] == v)
	    sp++;

	/* Try to find an operand-generating pc just above fp's variables. */
	depth = (intN)script->depth;
	base = fp->vars
	       ? fp->vars + fp->nvars
	       : (jsval *) cx->stackPool.current->base;
	if (JS_UPTRDIFF(sp - depth, base) < JS_UPTRDIFF(limit, base))
	    pc = (jsbytecode *) sp[-depth];
    }

    /* Be paranoid about loading an invalid pc from sp[-depth]. */
    if (!pc)
	goto do_fallback;

    /*
     * Using an object for which js_DefaultValue fails as part of an expression 
     * blows this assert.  Disabled for now.
     * JS_ASSERT(JS_UPTRDIFF(pc, script->code) < (jsuword)script->length);
     */
    if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
	pc = fp->pc;
	if (!pc)
	    goto do_fallback;
    }
    cs = &js_CodeSpec[*pc];
    format = cs->format;
    mode = (format & JOF_MODEMASK);

    /* NAME ops are self-contained, but others require left context. */
    if (mode == JOF_NAME) {
	begin = pc;
    } else {
	sn = js_GetSrcNote(script, pc);
	if (!sn || SN_TYPE(sn) != SRC_PCBASE)
	    goto do_fallback;
	begin = pc - js_GetSrcNoteOffset(sn, 0);
    }
    end = pc + cs->length;
    len =PTRDIFF(end, begin, jsbytecode);

    if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT)) {
	/* These formats require bytecode source extension. */
	maxoplen = js_CodeSpec[JSOP_GETPROP].length;
	tmp = JS_malloc(cx, (len + maxoplen) * sizeof(jsbytecode));
	if (!tmp)
	    return NULL;
	memcpy(tmp, begin, len * sizeof(jsbytecode));
	if (mode == JOF_NAME) {
	    tmp[0] = JSOP_NAME;
	} else {
	    if (begin[0] == JSOP_TRAP)
		tmp[0] = JS_GetTrapOpcode(cx, script, begin);
	    if (mode == JOF_PROP) {
		tmp[len++] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;
		tmp[len++] = pc[1];
		tmp[len++] = pc[2];
	    } else {
		tmp[len++] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM;
	    }
	}
	begin = tmp;
    } else {
	/* No need to extend script bytecode. */
	tmp = NULL;
    }

    jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0);
    if (jp && js_DecompileCode(jp, script, begin, len))
	name = js_GetPrinterOutput(jp);
    else
	name = NULL;
    js_DestroyPrinter(jp);
    if (tmp)
	JS_free(cx, tmp);
    return name;

  do_fallback:
    return fallback ? fallback : js_ValueToString(cx, v);
}

#ifdef _WINDOWS
#pragma optimize( "", on )
#endif

**** End of jsopcode.c. ****

**** Start of jsopcode.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsopcode_h___
#define jsopcode_h___
/*
 * JS bytecode definitions.
 */
#include <stddef.h>
#include "jsprvtd.h"
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

/*
 * JS operation bytecodes.
 */
typedef enum JSOp {
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
    op = val,
#include "jsopcode.tbl"
#undef OPDEF
    JSOP_LIMIT
} JSOp;

/*
 * JS bytecode formats.
 */
#define JOF_BYTE          0       /* single bytecode, no immediates */
#define JOF_JUMP          1       /* signed 16-bit jump offset immediate */
#define JOF_CONST         2       /* unsigned 16-bit constant pool index */
#define JOF_UINT16        3       /* unsigned 16-bit immediate operand */
#define JOF_TABLESWITCH   4       /* table switch */
#define JOF_LOOKUPSWITCH  5       /* lookup switch */
#define JOF_QARG          6       /* quickened get/set function argument ops */
#define JOF_QVAR          7       /* quickened get/set local variable ops */
#define JOF_TYPEMASK      0x000f  /* mask for above immediate types */
#define JOF_NAME          0x0010  /* name operation */
#define JOF_PROP          0x0020  /* obj.prop operation */
#define JOF_ELEM          0x0030  /* obj[index] operation */
#define JOF_MODEMASK      0x0030  /* mask for above addressing modes */
#define JOF_SET           0x0040  /* set (i.e., assignment) operation */
#define JOF_DEL           0x0080  /* delete operation */
#define JOF_DEC           0x0100  /* decrement (--, not ++) opcode */
#define JOF_INC           0x0200  /* increment (++, not --) opcode */
#define JOF_INCDEC        0x0300  /* increment or decrement opcode */
#define JOF_POST          0x0400  /* postorder increment or decrement */
#define JOF_IMPORT        0x0800  /* import property op */
#define JOF_FOR2          0x1000  /* new for/in loop bytecodes */

/*
 * Immediate operand getters, setters, and bounds.
 */
#define JUMP_OFFSET_LEN         2
#define JUMP_OFFSET_HI(off)     ((jsbytecode)((off) >> 8))
#define JUMP_OFFSET_LO(off)     ((jsbytecode)(off))
#define GET_JUMP_OFFSET(pc)     ((int16)(((pc)[1] << 8) | (pc)[2]))
#define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off),               \
				 (pc)[2] = JUMP_OFFSET_LO(off))
#define JUMP_OFFSET_MIN         ((int16)0x8000)
#define JUMP_OFFSET_MAX         ((int16)0x7fff)

#define ATOM_INDEX_LEN          2
#define ATOM_INDEX_HI(index)    ((jsbytecode)((index) >> 8))
#define ATOM_INDEX_LO(index)    ((jsbytecode)(index))
#define GET_ATOM_INDEX(pc)      (((pc)[1] << 8) | (pc)[2])
#define SET_ATOM_INDEX(pc,index)((pc)[1] = ATOM_INDEX_HI(index),              \
				 (pc)[2] = ATOM_INDEX_LO(index))
#define GET_ATOM(cx,script,pc)  js_GetAtom((cx), &(script)->atomMap,          \
					   GET_ATOM_INDEX(pc))
#define ATOM_INDEX_LIMIT_LOG2   16
#define ATOM_INDEX_LIMIT        ((uint32)1 << ATOM_INDEX_LIMIT_LOG2)

#define ARGC_HI(argc)           ((jsbytecode)((argc) >> 8))
#define ARGC_LO(argc)           ((jsbytecode)(argc))
#define GET_ARGC(pc)            (((pc)[1] << 8) | (pc)[2])
#define ARGC_LIMIT              ((uint32)1 << 16)

/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */
#define GET_ARGNO(pc)           GET_ARGC(pc)
#define SET_ARGNO(pc,argno)     SET_JUMP_OFFSET(pc,argno)
#define GET_VARNO(pc)           GET_ARGC(pc)
#define SET_VARNO(pc,varno)     SET_JUMP_OFFSET(pc,varno)

struct JSCodeSpec {
    const char          *name;          /* JS bytecode name */
    const char          *token;         /* JS source literal or null */
    int8                length;         /* length including opcode byte */
    int8                nuses;          /* arity, -1 if variadic */
    int8                ndefs;          /* number of stack results */
    uint8               prec;           /* operator precedence */
    uint32              format;         /* immediate operand format */
};

extern char             js_in_str[];
extern char             js_instanceof_str[];
extern char             js_new_str[];
extern char             js_delete_str[];
extern char             js_typeof_str[];
extern char             js_void_str[];
extern char             js_null_str[];
extern char             js_this_str[];
extern char             js_false_str[];
extern char             js_true_str[];
extern JSCodeSpec       js_CodeSpec[];
extern uintN            js_NumCodeSpecs;
extern jschar           js_EscapeMap[];

/*
 * Return a GC'ed string containing the chars in str, with any non-printing
 * chars or quotes (' or " as specified by the quote argument) escaped, and
 * with the quote character at the beginning and end of the result string.
 */
extern JSString *
js_QuoteString(JSContext *cx, JSString *str, jschar quote);

/*
 * JSPrinter operations, for printf style message formatting.  The return
 * value from js_GetPrinterOutput() is the printer's cumulative output, in
 * a GC'ed string.
 */
extern JSPrinter *
js_NewPrinter(JSContext *cx, const char *name, uintN indent);

extern void
js_DestroyPrinter(JSPrinter *jp);

extern JSString *
js_GetPrinterOutput(JSPrinter *jp);

extern int
js_printf(JSPrinter *jp, char *format, ...);

extern JSBool
js_puts(JSPrinter *jp, char *s);

#ifdef DEBUG
/*
 * Disassemblers, for debugging only.
 */
#include <stdio.h>

extern JS_FRIEND_API(void)
js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp);

extern JS_FRIEND_API(uintN)
js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
		JSBool lines, FILE *fp);
#endif /* DEBUG */

/*
 * Decompilers, for script, function, and expression pretty-printing.
 */
extern JSBool
js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len);

extern JSBool
js_DecompileScript(JSPrinter *jp, JSScript *script);

extern JSBool
js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun, JSBool newlines);

extern JSBool
js_DecompileFunction(JSPrinter *jp, JSFunction *fun, JSBool newlines);

/*
 * Find the source expression that resulted in v, and return a new string
 * containing it.  Fall back on v's string conversion if we can't find the
 * bytecode that generated and pushed v on the operand stack.
 */
extern JSString *
js_DecompileValueGenerator(JSContext *cx, jsval v, JSString *fallback);

JS_END_EXTERN_C

#endif /* jsopcode_h___ */

**** End of jsopcode.h. ****

**** Start of jsosdep.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsosdep_h___
#define jsosdep_h___
/*
 * OS (and machine, and compiler XXX) dependent information.
 */

#ifdef XP_PC

#ifdef _WIN32
#define JS_HAVE_LONG_LONG
#else
#undef JS_HAVE_LONG_LONG
#endif
#endif /* XP_PC */

#ifdef XP_MAC

JS_BEGIN_EXTERN_C

#include <stddef.h>

extern void* reallocSmaller(void* block, size_t newSize);

extern char* strdup(const char* str);

JS_END_EXTERN_C

#endif /* XP_MAC */

#ifdef XP_UNIX

/*
 * Get OS specific header information.
 */
#if defined(AIXV3)
#define JS_HAVE_LONG_LONG

#elif defined(BSDI)
#define JS_HAVE_LONG_LONG

#elif defined(HPUX)
#undef JS_HAVE_LONG_LONG

#elif defined(IRIX)
#define JS_HAVE_LONG_LONG

#elif defined(linux)
#define JS_HAVE_LONG_LONG

#elif defined(OSF1)
#define JS_HAVE_LONG_LONG

#elif defined(SCO)
#undef JS_HAVE_LONG_LONG

#elif defined(SOLARIS)
#define JS_HAVE_LONG_LONG

#elif defined(SUNOS4)
#undef JS_HAVE_LONG_LONG

/*
** Missing function prototypes
*/

extern void *sbrk(int);

#elif defined(UNIXWARE)
#undef JS_HAVE_LONG_LONG
#endif

#endif /* XP_UNIX */

#endif /* jsosdep_h___ */

**** End of jsosdep.h. ****

**** Start of jsotypes.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * This section typedefs the old 'native' types to the new PR<type>s.
 * These definitions are scheduled to be eliminated at the earliest
 * possible time. The NSPR API is implemented and documented using
 * the new definitions.
 */

/*
 * Note that we test for PROTYPES_H, not JSOTYPES_H.  This is to avoid
 * double-definitions of scalar types such as uint32, if NSPR's
 * protypes.h is also included.
 */
#ifndef PROTYPES_H
#define PROTYPES_H

/* SVR4 typedef of uint is commonly found on UNIX machines. */
#ifdef XP_UNIX
#include <sys/types.h>
#else
typedef JSUintn uint;
#endif

typedef JSUintn uintn;
typedef JSUint64 uint64;
#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2)
typedef JSUint32 uint32;
#else
typedef unsigned long uint32;
#endif
typedef JSUint16 uint16;
typedef JSUint8 uint8;

#ifndef _XP_Core_
typedef JSIntn intn;
#endif

/*
 * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very
 * common header file) defines the types int8, int16, int32, and int64.
 * So we don't define these four types here to avoid conflicts in case
 * the code also includes sys/types.h.
 */
#ifdef AIX4_3
#include <sys/inttypes.h>
#else
typedef JSInt64 int64;

/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */
#ifdef HPUX
#include <model.h>
#else
#if !defined(WIN32) || !defined(_WINSOCK2API_)  /* defines its own "int32" */
#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2)
typedef JSInt32 int32;
#else
typedef long int32;
#endif
#endif
typedef JSInt16 int16;
typedef JSInt8 int8;
#endif /* HPUX */
#endif /* AIX4_3 */

typedef JSFloat64 float64;

/* Re: jsbit.h */
#define TEST_BIT	JS_TEST_BIT
#define SET_BIT		JS_SET_BIT
#define CLEAR_BIT	JS_CLEAR_BIT

/* Re: prarena.h->plarena.h */
#define PRArena PLArena
#define PRArenaPool PLArenaPool
#define PRArenaStats PLArenaStats
#define PR_ARENA_ALIGN PL_ARENA_ALIGN
#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL
#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE
#define PR_ARENA_GROW PL_ARENA_GROW
#define PR_ARENA_MARK PL_ARENA_MARK
#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED
#define PR_CLEAR_ARENA PL_CLEAR_ARENA
#define PR_ARENA_RELEASE PL_ARENA_RELEASE
#define PR_COUNT_ARENA PL_COUNT_ARENA
#define PR_ARENA_DESTROY PL_ARENA_DESTROY
#define PR_InitArenaPool PL_InitArenaPool
#define PR_FreeArenaPool PL_FreeArenaPool
#define PR_FinishArenaPool PL_FinishArenaPool
#define PR_CompactArenaPool PL_CompactArenaPool
#define PR_ArenaFinish PL_ArenaFinish
#define PR_ArenaAllocate PL_ArenaAllocate
#define PR_ArenaGrow PL_ArenaGrow
#define PR_ArenaRelease PL_ArenaRelease
#define PR_ArenaCountAllocation PL_ArenaCountAllocation
#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth
#define PR_ArenaCountGrowth PL_ArenaCountGrowth
#define PR_ArenaCountRelease PL_ArenaCountRelease
#define PR_ArenaCountRetract PL_ArenaCountRetract

/* Re: prevent.h->plevent.h */
#define PREvent PLEvent
#define PREventQueue PLEventQueue
#define PR_CreateEventQueue PL_CreateEventQueue
#define PR_DestroyEventQueue PL_DestroyEventQueue
#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor
#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR
#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR
#define PR_PostEvent PL_PostEvent
#define PR_PostSynchronousEvent PL_PostSynchronousEvent
#define PR_GetEvent PL_GetEvent
#define PR_EventAvailable PL_EventAvailable
#define PREventFunProc PLEventFunProc
#define PR_MapEvents PL_MapEvents
#define PR_RevokeEvents PL_RevokeEvents
#define PR_ProcessPendingEvents PL_ProcessPendingEvents
#define PR_WaitForEvent PL_WaitForEvent
#define PR_EventLoop PL_EventLoop
#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD
#define PRHandleEventProc PLHandleEventProc
#define PRDestroyEventProc PLDestroyEventProc
#define PR_InitEvent PL_InitEvent
#define PR_GetEventOwner PL_GetEventOwner
#define PR_HandleEvent PL_HandleEvent
#define PR_DestroyEvent PL_DestroyEvent
#define PR_DequeueEvent PL_DequeueEvent
#define PR_GetMainEventQueue PL_GetMainEventQueue

/* Re: prhash.h->plhash.h */
#define PRHashEntry PLHashEntry
#define PRHashTable PLHashTable
#define PRHashNumber PLHashNumber
#define PRHashFunction PLHashFunction
#define PRHashComparator PLHashComparator
#define PRHashEnumerator PLHashEnumerator
#define PRHashAllocOps PLHashAllocOps
#define PR_NewHashTable PL_NewHashTable
#define PR_HashTableDestroy PL_HashTableDestroy
#define PR_HashTableRawLookup PL_HashTableRawLookup
#define PR_HashTableRawAdd PL_HashTableRawAdd
#define PR_HashTableRawRemove PL_HashTableRawRemove
#define PR_HashTableAdd PL_HashTableAdd
#define PR_HashTableRemove PL_HashTableRemove
#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries
#define PR_HashTableLookup PL_HashTableLookup
#define PR_HashTableDump PL_HashTableDump
#define PR_HashString PL_HashString
#define PR_CompareStrings PL_CompareStrings
#define PR_CompareValues PL_CompareValues

#ifdef XP_MAC
#ifndef TRUE				/* Mac standard is lower case true */
	#define TRUE 1
#endif
#ifndef FALSE				/* Mac standard is lower case false */
	#define FALSE 0
#endif
#endif

#endif /* !defined(PROTYPES_H) */

**** End of jsotypes.h. ****

**** Start of jsparse.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS parser.
 *
 * This is a recursive-descent parser for the JavaScript language specified by
 * "The JavaScript 1.4 Language Specification".  It uses lexical and semantic
 * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes
 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
 * After tree construction, it rewrites trees to fold constants and evaluate
 * compile-time expressions.  Finally, it calls js_EmitTree (see jsemit.h) to
 * generate bytecode.
 *
 * This parser attempts no error recovery.  The dense JSTokenType enumeration
 * was designed with error recovery built on 64-bit first and follow bitsets
 * in mind, however.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"

/*
 * JS parsers, from lowest to highest precedence.
 *
 * Each parser takes a context and a token stream, and emits bytecode using
 * a code generator.
 */

typedef JSParseNode *
JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
typedef JSParseNode *
JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
	       JSBool allowCallSyntax);

static JSParser FunctionStmt;
#if JS_HAS_LEXICAL_CLOSURE
static JSParser FunctionExpr;
#endif
static JSParser Statements;
static JSParser Statement;
static JSParser Variables;
static JSParser Expr;
static JSParser AssignExpr;
static JSParser CondExpr;
static JSParser OrExpr;
static JSParser AndExpr;
static JSParser BitOrExpr;
static JSParser BitXorExpr;
static JSParser BitAndExpr;
static JSParser EqExpr;
static JSParser RelExpr;
static JSParser ShiftExpr;
static JSParser AddExpr;
static JSParser MulExpr;
static JSParser UnaryExpr;
static JSMemberParser MemberExpr;
static JSParser PrimaryExpr;

/*
 * Insist that the next token be of type tt, or report err and throw or fail.
 * NB: this macro uses cx and ts from its lexical environment.
 */

#define MUST_MATCH_TOKEN_THROW(tt, errno, throw)                              \
    JS_BEGIN_MACRO                                                            \
	if (js_GetToken(cx, ts) != tt) {                                      \
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR, errno);       \
	    throw;                                                            \
	}                                                                     \
    JS_END_MACRO

#define MUST_MATCH_TOKEN(tt, errno)                                           \
    MUST_MATCH_TOKEN_THROW(tt, errno, return NULL)


/*
 * Allocate a JSParseNode from cx's temporary arena.
 */
static JSParseNode *
NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity)
{
    JSParseNode *pn;

    JS_ARENA_ALLOCATE(pn, &cx->tempPool, sizeof(JSParseNode));
    if (!pn)
	return NULL;
    pn->pn_type = tok->type;
    pn->pn_pos = tok->pos;
    pn->pn_arity = arity;
    pn->pn_next = NULL;
    return pn;
}

static JSParseNode *
NewBinary(JSContext *cx, JSTokenType tt,
	  JSOp op, JSParseNode *left, JSParseNode *right)
{
    JSParseNode *pn;

    if (!left || !right)
	return NULL;
    JS_ARENA_ALLOCATE(pn, &cx->tempPool, sizeof(JSParseNode));
    if (!pn)
	return NULL;
    pn->pn_type = tt;
    pn->pn_pos.begin = left->pn_pos.begin;
    pn->pn_pos.end = right->pn_pos.end;
    pn->pn_op = op;
    pn->pn_arity = PN_BINARY;
    pn->pn_left = left;
    pn->pn_right = right;
    pn->pn_next = NULL;
    return pn;
}

static JSBool
WellTerminated(JSContext *cx, JSTokenStream *ts, JSTokenType lastExprType)
{
    JSTokenType tt;

    tt = js_PeekTokenSameLine(cx, ts);
    if (tt == TOK_ERROR)
	return JS_FALSE;
    if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
#if JS_HAS_LEXICAL_CLOSURE
	if ((tt == TOK_FUNCTION || lastExprType == TOK_FUNCTION) &&
	    cx->version < JSVERSION_1_2) {
	    /*
	     * Checking against version < 1.2 and version >= 1.0
	     * in the above line breaks old javascript, so we keep it
	     * this way for now... XXX warning needed?
	     */

	    return JS_TRUE;
	}
#endif
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
				    JSMSG_SEMI_BEFORE_STMNT);
	return JS_FALSE;
    }
    return JS_TRUE;
}

/*
 * Parse a top-level JS script.
 */
JS_FRIEND_API(JSBool)
js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
		      JSCodeGenerator *cg)
{
    JSStackFrame *fp, frame;
    JSTokenType stop, tt;
    JSBool ok;
    JSParseNode *pn;

    fp = cx->fp;
    if (!fp || fp->scopeChain != chain) {
	memset(&frame, 0, sizeof frame);
	frame.scopeChain = chain;
	frame.down = fp;
	cx->fp = &frame;
    }

    if (ts->flags & TSF_INTERACTIVE) {
	SCAN_NEWLINES(ts);
	stop = TOK_EOL;
    } else {
	stop = TOK_EOF;
    }

    /*
     * Prevent GC activation on this context (possible if out of memory when
     * atomizing, or from pre-ECMAv2 switch case expr eval in the unlikely
     * case of a branch-callback -- unlikely because it means the switch case
     * must have called a function).
     */
    cx->gcDisabled++;

    ok = JS_TRUE;
    do {
	ts->flags |= TSF_REGEXP;
	tt = js_GetToken(cx, ts);
	ts->flags &= ~TSF_REGEXP;
	if (tt == stop || tt <= TOK_EOF) {
	    if (tt == TOK_ERROR)
		ok = JS_FALSE;
	    break;
	}

	switch (tt) {
	  case TOK_FUNCTION:
	    pn = FunctionStmt(cx, ts, &cg->treeContext);
	    if (pn && pn->pn_pos.end.lineno == ts->lineno) {
		ok = WellTerminated(cx, ts, TOK_FUNCTION);
		if (!ok)
		    goto out;
	    }
	    break;

	  default:
	    js_UngetToken(ts);
	    pn = Statement(cx, ts, &cg->treeContext);
	    if (pn) {
		ok = js_FoldConstants(cx, pn);
		if (!ok)
		    goto out;
	    }
	    break;
	}
	if (pn) {
	    ok = js_AllocTryNotes(cx, cg);
	    if (ok)
		ok = js_EmitTree(cx, cg, pn);
	} else {
	    ok = JS_FALSE;
	}
    } while (ok);

out:
    ts->flags &= ~TSF_BADCOMPILE;
    cx->gcDisabled--;
    cx->fp = fp;
    if (!ok)
	CLEAR_PUSHBACK(ts);
    return ok;
}

#ifdef CHECK_RETURN_EXPR

/*
 * Insist on a final return before control flows out of pn, but don't be too
 * smart about loops (do {...; return e2;} while(0) at the end of a function
 * that contains an early return e1 will get an error XXX should be warning
 * option).
 */
static JSBool
CheckFinalReturn(JSParseNode *pn)
{
    JSBool ok, hasDefault;
    JSParseNode *pn2, *pn3;

    switch (pn->pn_type) {
      case TOK_LC:
	if (!pn->pn_head)
	    return JS_FALSE;
	return CheckFinalReturn(PN_LAST(pn));
      case TOK_IF:
	ok = CheckFinalReturn(pn->pn_kid2);
	ok &= pn->pn_kid3 && CheckFinalReturn(pn->pn_kid3);
	return ok;
#if JS_HAS_SWITCH_STATEMENT
      case TOK_SWITCH:
	ok = JS_TRUE;
	for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) {
	    if (pn2->pn_type == TOK_DEFAULT)
		hasDefault = JS_TRUE;
	    pn3 = pn2->pn_right;
	    JS_ASSERT(pn3->pn_type == TOK_LC);
	    if (pn3->pn_head)
		ok &= CheckFinalReturn(PN_LAST(pn3));
	}
	/* If a final switch has no default case, we judge it harshly. */
	ok &= hasDefault;
	return ok;
#endif /* JS_HAS_SWITCH_STATEMENT */
      case TOK_WITH:
	return CheckFinalReturn(pn->pn_right);
      case TOK_RETURN:
	return JS_TRUE;
      default:
	return JS_FALSE;
    }
}

#endif /* CHECK_RETURN_EXPR */

static JSParseNode *
FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
	     JSTreeContext *tc)
{
    JSStackFrame *fp, frame;
    uintN oldflags;
    JSParseNode *pn;

    fp = cx->fp;
    if (!fp || fp->scopeChain != fun->object) {
	memset(&frame, 0, sizeof frame);
	frame.scopeChain = fun->object;
	frame.down = fp;
	cx->fp = &frame;
    }

    oldflags = tc->flags;
    tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
    tc->flags |= TCF_IN_FUNCTION;
    pn = Statements(cx, ts, tc);

#ifdef CHECK_RETURN_EXPR
    /* Check for falling off the end of a function that returns a value. */
    if (pn && (tc->flags & TCF_RETURN_EXPR)) {
	if (!CheckFinalReturn(pn)) {
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					JSMSG_NO_RETURN_VALUE);
	    pn = NULL;
	}
    }
#endif

    cx->fp = fp;
    tc->flags = oldflags;
    return pn;
}

/*
 * Compile a JS function body, which might appear as the value of an event
 * handler attribute in an HTML <INPUT> tag.
 */
JSBool
js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
{
    JSCodeGenerator funcg;
    JSParseNode *pn;
    JSBool ok;

    if (!js_InitCodeGenerator(cx, &funcg, ts->filename, ts->lineno,
			      ts->principals)) {
	return JS_FALSE;
    }

    /* Prevent GC activation on this context during compilation. */
    cx->gcDisabled++;

    /* Satisfy the assertion at the top of Statements. */
    ts->token.type = TOK_LC;
    pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
    if (!pn) {
	CLEAR_PUSHBACK(ts);
	ok = JS_FALSE;
    } else {
	ok = js_FoldConstants(cx, pn);
	if (ok)
	    ok = js_EmitFunctionBody(cx, &funcg, pn, fun);
    }

    cx->gcDisabled--;
    js_FinishCodeGenerator(cx, &funcg);
    return ok;
}

static JSBool
InWithStatement(JSTreeContext *tc)
{
    JSStmtInfo *stmt;

    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
	if (stmt->type == STMT_WITH)
	    return JS_TRUE;
    }
    return JS_FALSE;
}

static JSParseNode *
FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
	    JSBool lambda)
{
    JSParseNode *pn, *pn2;
    JSAtom *funAtom, *argAtom;
    JSObject *parent;
    JSFunction *fun, *outerFun;
    JSBool ok, named;
    JSObject *pobj;
    JSScopeProperty *sprop;
    JSTreeContext funtc;
    jsval junk;

    /* Make a TOK_FUNCTION node. */
    pn = NewParseNode(cx, &ts->token, PN_FUNC);
    if (!pn)
	return NULL;

    /* Scan the optional function name into funAtom. */
    if (js_MatchToken(cx, ts, TOK_NAME))
	funAtom = ts->token.t_atom;
    else
	funAtom = NULL;

    /* Find the nearest variable-declaring scope and use it as our parent. */
    parent = js_FindVariableScope(cx, &outerFun);
    if (!parent)
	return NULL;

#if JS_HAS_LEXICAL_CLOSURE
    if (!funAtom || cx->fp->scopeChain != parent || InWithStatement(tc)) {
	/* Don't name the function if enclosed by a with statement or equiv. */
	fun = js_NewFunction(cx, NULL, NULL, 0, 0, cx->fp->scopeChain,
			     funAtom);
	named = JS_FALSE;
    } else
#endif
    {
	/* Override any previously defined property using js_DefineFunction. */
	fun = js_DefineFunction(cx, parent, funAtom, NULL, 0, JSPROP_ENUMERATE);
	named = (fun != NULL);
    }
    if (!fun) {
	ok = JS_FALSE;
	goto out;
    }

    /* Now parse formal argument list and compute fun->nargs. */
    MUST_MATCH_TOKEN_THROW(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL,
			   ok = JS_FALSE; goto out);
    if (!js_MatchToken(cx, ts, TOK_RP)) {
	do {
	    MUST_MATCH_TOKEN_THROW(TOK_NAME, JSMSG_MISSING_FORMAL,
				   ok = JS_FALSE; goto out);
	    argAtom = ts->token.t_atom;
	    pobj = NULL;
	    ok = js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,
				   (JSProperty **)&sprop);
	    if (!ok)
		goto out;
	    if (sprop && pobj == fun->object) {
		if (sprop->getter == js_GetArgument) {
#ifdef CHECK_ARGUMENT_HIDING
		    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_WARNING,
						JSMSG_DUPLICATE_FORMAL,
						ATOM_BYTES(argAtom));
		    ok = JS_FALSE;
		    goto out;
#else
		    /*
		     * A duplicate parameter name. We create a dummy symbol
		     * entry with property id of the parameter number and set
		     * the id to the name of the parameter.
		     * The decompiler will know to treat this case specially.
		     */
		    jsid oldArgId = (jsid) sprop->id;
		    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
		    sprop = NULL;
		    ok = js_DefineProperty(cx, fun->object,
					   oldArgId, JSVAL_VOID,
					   js_GetArgument, js_SetArgument,
					   JSPROP_ENUMERATE | JSPROP_PERMANENT,
					   (JSProperty **)&sprop);
		    if (!ok)
			goto out;
		    sprop->id = (jsid) argAtom;
#endif
		}
	    }
	    if (sprop) {
		OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
		sprop = NULL;
	    }
	    ok = js_DefineProperty(cx, fun->object,
				   (jsid)argAtom, JSVAL_VOID,
				   js_GetArgument, js_SetArgument,
				   JSPROP_ENUMERATE | JSPROP_PERMANENT,
				   (JSProperty **)&sprop);
	    if (!ok)
		goto out;
	    JS_ASSERT(sprop);
	    sprop->id = INT_TO_JSVAL(fun->nargs++);
	    OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop);
	} while (js_MatchToken(cx, ts, TOK_COMMA));

	MUST_MATCH_TOKEN_THROW(TOK_RP, JSMSG_PAREN_AFTER_FORMAL,
			       ok = JS_FALSE; goto out);
    }

    MUST_MATCH_TOKEN_THROW(TOK_LC, JSMSG_CURLY_BEFORE_BODY,
			   ok = JS_FALSE; goto out);
    pn->pn_pos.begin = ts->token.pos.begin;

    TREE_CONTEXT_INIT(&funtc);
    pn2 = FunctionBody(cx, ts, fun, &funtc);
    if (!pn2) {
	ok = JS_FALSE;
	goto out;
    }

    MUST_MATCH_TOKEN_THROW(TOK_RC, JSMSG_CURLY_AFTER_BODY,
			   ok = JS_FALSE; goto out);
    pn->pn_pos.end = ts->token.pos.end;

    pn->pn_fun = fun;
    pn->pn_body = pn2;
    pn->pn_tryCount = funtc.tryCount;

#if JS_HAS_LEXICAL_CLOSURE
    if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc))
	pn->pn_op = JSOP_CLOSURE;
    else if (lambda)
	pn->pn_op = JSOP_OBJECT;
    else
#endif
	pn->pn_op = JSOP_NOP;

    ok = JS_TRUE;
out:
    if (!ok) {
	if (named)
	    (void) OBJ_DELETE_PROPERTY(cx, parent, (jsid)funAtom, &junk);
	return NULL;
    }
    return pn;
}

static JSParseNode *
FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    return FunctionDef(cx, ts, tc, JS_FALSE);
}

#if JS_HAS_LEXICAL_CLOSURE
static JSParseNode *
FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    return FunctionDef(cx, ts, tc, JS_TRUE);
}
#endif

/*
 * Parse the statements in a block, creating a TOK_LC node that lists the
 * statements' trees.  Our caller must match { before and } after.
 */
static JSParseNode *
Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn2;
    uintN newlines;
    JSTokenType tt;

    JS_ASSERT(ts->token.type == TOK_LC);
    pn = NewParseNode(cx, &ts->token, PN_LIST);
    if (!pn)
	return NULL;
    PN_INIT_LIST(pn);

    newlines = ts->flags & TSF_NEWLINES;
    if (newlines)
	HIDE_NEWLINES(ts);

    while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
	pn2 = Statement(cx, ts, tc);
	if (!pn2) {
	    pn = NULL;
	    goto out;
	}
	PN_APPEND(pn, pn2);
    }

    pn->pn_pos.end = ts->token.pos.end;
    if (tt == TOK_ERROR)
	pn = NULL;
out:
    if (newlines)
	SCAN_NEWLINES(ts);
    return pn;
}

static JSParseNode *
Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn2;

    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
    pn = Expr(cx, ts, tc);
    if (!pn)
	return NULL;
    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);

    /*
     * Check for (a = b) and "correct" it to (a == b).
     * XXX not ECMA, but documented in several books -- need a compile option
     */
    if (pn->pn_type == TOK_ASSIGN && pn->pn_op == JSOP_NOP) {
	js_ReportCompileErrorNumber(cx, ts,JSREPORT_WARNING,
				    JSMSG_EQUAL_AS_ASSIGN,
				    JSVERSION_IS_ECMA(cx->version) ? ""
				    : "\nAssuming equality test");
	if (JSVERSION_IS_ECMA(cx->version))
	    goto no_rewrite;
	pn->pn_type = TOK_EQOP;
	pn->pn_op = cx->jsop_eq;
	pn2 = pn->pn_left;
	switch (pn2->pn_op) {
	  case JSOP_SETARG:
	    pn2->pn_op = JSOP_GETARG;
	    break;
	  case JSOP_SETVAR:
	    pn2->pn_op = JSOP_GETVAR;
	    break;
	  case JSOP_SETNAME2:
	    pn2->pn_op = JSOP_NAME;
	    break;
	  case JSOP_SETPROP:
	    pn2->pn_op = JSOP_GETPROP;
	    break;
	  case JSOP_SETELEM:
	    pn2->pn_op = JSOP_GETELEM;
	    break;
	  default:
	    JS_ASSERT(0);
	}
    }
  no_rewrite:
    return pn;
}

static JSBool
MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
{
    JSAtom *label;
#if JS_HAS_LABEL_STATEMENT
    JSTokenType tt;

    tt = js_PeekTokenSameLine(cx, ts);
    if (tt == TOK_ERROR)
	return JS_FALSE;
    if (tt == TOK_NAME) {
	(void) js_GetToken(cx, ts);
	label = ts->token.t_atom;
    } else {
	label = NULL;
    }
#else
    label = NULL;
#endif
    pn->pn_atom = label;
    if (pn->pn_pos.end.lineno == ts->lineno)
	return WellTerminated(cx, ts, TOK_ERROR);
    return JS_TRUE;
}

#if JS_HAS_EXPORT_IMPORT
static JSParseNode *
ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn2, *pn3;
    JSTokenType tt;

    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
    pn = NewParseNode(cx, &ts->token, PN_NULLARY);
    if (!pn)
	return NULL;
    pn->pn_op = JSOP_NAME;
    pn->pn_atom = ts->token.t_atom;
    pn->pn_slot = -1;

    ts->flags |= TSF_REGEXP;
    while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
	ts->flags &= ~TSF_REGEXP;
	if (pn->pn_op == JSOP_IMPORTALL)
	    goto bad_import;

	if (tt == TOK_DOT) {
	    pn2 = NewParseNode(cx, &ts->token, PN_NAME);
	    if (!pn2)
		return NULL;
	    pn2->pn_expr = pn;
	    if (js_MatchToken(cx, ts, TOK_STAR)) {
		pn2->pn_op = JSOP_IMPORTALL;
		pn2->pn_atom = NULL;
	    } else {
		MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
		pn2->pn_op = JSOP_GETPROP;
		pn2->pn_atom = ts->token.t_atom;
		pn2->pn_slot = -1;
	    }
	    pn2->pn_pos.begin = pn->pn_pos.begin;
	    pn2->pn_pos.end = ts->token.pos.end;
	} else {
	    /* Make a TOK_LB node. */
	    pn2 = NewParseNode(cx, &ts->token, PN_BINARY);
	    if (!pn2)
		return NULL;
	    pn3 = Expr(cx, ts, tc);
	    if (!pn3)
		return NULL;

	    MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
	    pn2->pn_pos.begin = pn->pn_pos.begin;
	    pn2->pn_pos.end = ts->token.pos.end;

            pn2->pn_op = JSOP_GETELEM;
            pn2->pn_left = pn;
            pn2->pn_right = pn3;
	}

	pn = pn2;
	ts->flags |= TSF_REGEXP;
    }
    ts->flags &= ~TSF_REGEXP;
    if (tt == TOK_ERROR)
	return NULL;
    js_UngetToken(ts);

    switch (pn->pn_op) {
      case JSOP_GETPROP:
	pn->pn_op = JSOP_IMPORTPROP;
	break;
      case JSOP_GETELEM:
	pn->pn_op = JSOP_IMPORTELEM;
	break;
      case JSOP_IMPORTALL:
	break;
      default:
	goto bad_import;
    }
    return pn;

  bad_import:
    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR, JSMSG_BAD_IMPORT);
    return NULL;
}
#endif /* JS_HAS_EXPORT_IMPORT */

static JSParseNode *
Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSTokenType tt, lastExprType;
    JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
    JSStmtInfo stmtInfo, *stmt, *stmt2;
    JSAtom *label;

    ts->flags |= TSF_REGEXP;
    tt = js_GetToken(cx, ts);
    ts->flags &= ~TSF_REGEXP;

    switch (tt) {
#if JS_HAS_EXPORT_IMPORT
      case TOK_EXPORT:
	pn = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn)
	    return NULL;
	PN_INIT_LIST(pn);
	if (js_MatchToken(cx, ts, TOK_STAR)) {
	    pn2 = NewParseNode(cx, &ts->token, PN_NULLARY);
	    if (!pn2)
		return NULL;
	    PN_APPEND(pn, pn2);
	} else {
	    do {
		MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
		pn2 = NewParseNode(cx, &ts->token, PN_NULLARY);
		if (!pn2)
		    return NULL;
		pn2->pn_op = JSOP_NAME;
		pn2->pn_atom = ts->token.t_atom;
		pn2->pn_slot = -1;
		PN_APPEND(pn, pn2);
	    } while (js_MatchToken(cx, ts, TOK_COMMA));
	}
	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
	if (pn->pn_pos.end.lineno == ts->lineno &&
	    !WellTerminated(cx, ts, TOK_ERROR)) {
	    return NULL;
	}
	break;

      case TOK_IMPORT:
	pn = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn)
	    return NULL;
	PN_INIT_LIST(pn);
	do {
	    pn2 = ImportExpr(cx, ts, tc);
	    if (!pn2)
		return NULL;
	    PN_APPEND(pn, pn2);
	} while (js_MatchToken(cx, ts, TOK_COMMA));
	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
	if (pn->pn_pos.end.lineno == ts->lineno &&
	    !WellTerminated(cx, ts, TOK_ERROR)) {
	    return NULL;
	}
	break;
#endif /* JS_HAS_EXPORT_IMPORT */

      case TOK_IF:
	/* An IF node has three kids: condition, then, and optional else. */
	pn = NewParseNode(cx, &ts->token, PN_TERNARY);
	if (!pn)
	    return NULL;
	pn1 = Condition(cx, ts, tc);
	if (!pn1)
	    return NULL;
	js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
	pn2 = Statement(cx, ts, tc);
	if (!pn2)
	    return NULL;
	if (js_MatchToken(cx, ts, TOK_ELSE)) {
	    stmtInfo.type = STMT_ELSE;
	    pn3 = Statement(cx, ts, tc);
	    if (!pn3)
		return NULL;
	    pn->pn_pos.end = pn3->pn_pos.end;
	} else {
	    pn3 = NULL;
	    pn->pn_pos.end = pn2->pn_pos.end;
	}
	js_PopStatement(tc);
	pn->pn_kid1 = pn1;
	pn->pn_kid2 = pn2;
	pn->pn_kid3 = pn3;
	return pn;

#if JS_HAS_SWITCH_STATEMENT
      case TOK_SWITCH:
      {
	uintN newlines;
	JSParseNode *pn5;
	JSBool seenDefault = JS_FALSE;

	pn = NewParseNode(cx, &ts->token, PN_BINARY);
	if (!pn)
	    return NULL;
	MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);

	/* pn1 points to the switch's discriminant. */
	pn1 = Expr(cx, ts, tc);
	if (!pn1)
	    return NULL;

	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
	MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);

	/* pn2 is a list of case nodes. The default case has pn_left == NULL */
	pn2 = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn2)
	    return NULL;
	PN_INIT_LIST(pn2);

	js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
	newlines = ts->flags & TSF_NEWLINES;
	if (newlines)
	    HIDE_NEWLINES(ts);

	while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
	    switch (tt) {
	      case TOK_DEFAULT:
		if (seenDefault) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_TOO_MANY_DEFAULTS);
		    goto bad_switch;
		}
		seenDefault = JS_TRUE;
		/* fall through */

	      case TOK_CASE:
		pn3 = NewParseNode(cx, &ts->token, PN_BINARY);
		if (!pn3)
		    goto bad_switch;
		if (tt == TOK_DEFAULT) {
		    pn3->pn_left = NULL;
		} else {
		    pn3->pn_left = Expr(cx, ts, tc);
		    if (!pn3->pn_left)
			goto bad_switch;
		}
		PN_APPEND(pn2, pn3);
		if (pn2->pn_count == JS_BIT(16)) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_TOO_MANY_CASES);
		    goto bad_switch;
		}
		break;

	      case TOK_ERROR:
		goto bad_switch;

	      default:
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					    JSMSG_BAD_SWITCH);
		goto bad_switch;
	    }
	    MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);

	    pn4 = NewParseNode(cx, &ts->token, PN_LIST);
	    if (!pn4)
		goto bad_switch;
	    pn4->pn_type = TOK_LC;
	    PN_INIT_LIST(pn4);
	    while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
		    tt != TOK_CASE && tt != TOK_DEFAULT) {
		if (tt == TOK_ERROR)
		    goto bad_switch;
		pn5 = Statement(cx, ts, tc);
		if (!pn5)
		    goto bad_switch;
		pn4->pn_pos.end = pn5->pn_pos.end;
		PN_APPEND(pn4, pn5);
	    }
	    pn3->pn_pos.end = pn4->pn_pos.end;
	    pn3->pn_right = pn4;
	}

	if (newlines)
	    SCAN_NEWLINES(ts);
	js_PopStatement(tc);

	pn->pn_pos.end = pn2->pn_pos.end = ts->token.pos.end;
	pn->pn_kid1 = pn1;
	pn->pn_kid2 = pn2;
	return pn;

      bad_switch:
	if (newlines)
	    SCAN_NEWLINES(ts);
	return NULL;
      }
#endif /* JS_HAS_SWITCH_STATEMENT */

      case TOK_WHILE:
	pn = NewParseNode(cx, &ts->token, PN_BINARY);
	if (!pn)
	    return NULL;
	js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
	pn2 = Condition(cx, ts, tc);
	if (!pn2)
	    return NULL;
	pn->pn_left = pn2;
	pn2 = Statement(cx, ts, tc);
	if (!pn2)
	    return NULL;
	js_PopStatement(tc);
	pn->pn_pos.end = pn2->pn_pos.end;
	pn->pn_right = pn2;
	return pn;

#if JS_HAS_DO_WHILE_LOOP
      case TOK_DO:
	pn = NewParseNode(cx, &ts->token, PN_BINARY);
	if (!pn)
	    return NULL;
	js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
	pn2 = Statement(cx, ts, tc);
	if (!pn2)
	    return NULL;
	pn->pn_left = pn2;
	MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
	pn2 = Condition(cx, ts, tc);
	if (!pn2)
	    return NULL;
	js_PopStatement(tc);
	pn->pn_pos.end = pn2->pn_pos.end;
	pn->pn_right = pn2;
	break;
#endif /* JS_HAS_DO_WHILE_LOOP */

      case TOK_FOR:
	/* A FOR node is binary, left is loop control and right is the body. */
	pn = NewParseNode(cx, &ts->token, PN_BINARY);
	if (!pn)
	    return NULL;
	js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);

	MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
	tt = js_PeekToken(cx, ts);
	if (tt == TOK_SEMI) {
	    /* No initializer -- set first kid of left sub-node to null. */
	    pn1 = NULL;
	} else {
	    /* Set pn1 to a var list or an initializing expression. */
#if JS_HAS_IN_OPERATOR
	    /*
	     * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
	     * of the for statement.  This flag will be used by the RelExpr
	     * production; if it is set, then the 'in' keyword will not be
	     * recognized as an operator, leaving it available to be parsed as
	     * part of a for/in loop.  A side effect of this restriction is
	     * that (unparenthesized) expressions involving an 'in' operator
	     * are illegal in the init clause of an ordinary for loop.
	     */
	    tc->flags |= TCF_IN_FOR_INIT;
#endif /* JS_HAS_IN_OPERATOR */
	    if (tt == TOK_VAR) {
		(void) js_GetToken(cx, ts);
		pn1 = Variables(cx, ts, tc);
	    } else {
		pn1 = Expr(cx, ts, tc);
	    }
#if JS_HAS_IN_OPERATOR
	    tc->flags &= ~TCF_IN_FOR_INIT;
#endif /* JS_HAS_IN_OPERATOR */
	    if (!pn1)
		return NULL;
	}

	/*
	 * We can be sure that if it's a for/in loop, there's still an 'in'
	 * keyword here, even if Javascript recognizes it as an operator,
	 * because we've excluded it from parsing by setting the
	 * TCF_IN_FOR_INIT flag on the JSTreeContext argument.
	 */
	if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
	    stmtInfo.type = STMT_FOR_IN_LOOP;

	    /* Check that the left side of the 'in' is valid. */
	    if (pn1->pn_type != TOK_VAR &&
		pn1->pn_type != TOK_NAME &&
		pn1->pn_type != TOK_DOT &&
		pn1->pn_type != TOK_LB) {
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					    JSMSG_BAD_FOR_LEFTSIDE);
		return NULL;
	    }

	    /* Parse the object expression as the right operand of 'in'. */
	    pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc));
	    if (!pn2)
		return NULL;
	    pn->pn_left = pn2;
	} else {
	    /* Parse the loop condition or null into pn2. */
	    MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
	    if (js_PeekToken(cx, ts) == TOK_SEMI) {
		pn2 = NULL;
	    } else {
		pn2 = Expr(cx, ts, tc);
		if (!pn2)
		    return NULL;
	    }

	    /* Parse the update expression or null into pn3. */
	    MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
	    if (js_PeekToken(cx, ts) == TOK_RP) {
		pn3 = NULL;
	    } else {
		pn3 = Expr(cx, ts, tc);
		if (!pn3)
		    return NULL;
	    }

	    /* Build the RESERVED node to use as the left kid of pn. */
	    pn4 = NewParseNode(cx, &ts->token, PN_TERNARY);
	    if (!pn4)
		return NULL;
	    pn4->pn_type = TOK_RESERVED;
	    pn4->pn_kid1 = pn1;
	    pn4->pn_kid2 = pn2;
	    pn4->pn_kid3 = pn3;
	    pn->pn_left = pn4;
	}

	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);

	/* Parse the loop body into pn->pn_right. */
	pn2 = Statement(cx, ts, tc);
	if (!pn2)
	    return NULL;
	pn->pn_right = pn2;
	js_PopStatement(tc);

	/* Record the absolute line number for source note emission. */
	pn->pn_pos.end = pn2->pn_pos.end;
	return pn;

#if JS_HAS_EXCEPTIONS
      case TOK_TRY: {
	JSParseNode *catchtail = NULL;
	/*
	 * try nodes are ternary.
	 * kid1 is the try Statement
	 * kid2 is the catch node
	 * kid3 is the finally Statement
	 *
	 * catch nodes are ternary.
	 * kid1 is the discriminant
	 * kid2 is the next catch node, or NULL
	 * kid3 is the catch block (on kid3 so that we can always append a
	 *                          new catch pn on catchtail->kid2)
	 *
	 * catch discriminant nodes are binary
	 * atom is the receptacle
	 * expr is the discriminant code
	 *
	 * finally nodes are unary (just the finally expression)
	 */
	pn = NewParseNode(cx, &ts->token, PN_TERNARY);
	pn->pn_op = JSOP_NOP;

	MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
	pn->pn_kid1 = Statements(cx, ts, tc);
	if (!pn->pn_kid1)
	    return NULL;
	MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);

	pn->pn_kid2 = NULL;
	catchtail = pn;
	while(js_PeekToken(cx, ts) == TOK_CATCH) {
	    /*
	     * legal catch form is:
	     * catch (v)
             * 
             * The form
	     * catch (v : <boolean_expression>)
             * has been pulled pending resolution in ECMA.
	     */

	    /* catch node */
	    pn2 = NewParseNode(cx, &ts->token, PN_TERNARY);
	    pn2->pn_op = JSOP_NOP;

	    /*
	     * We use a PN_NAME for the discriminant (catchguard) node
	     * with the actual discriminant code in the initializer spot
	     */
	    pn3 = NewParseNode(cx, &ts->token, PN_NAME);
	    if (!pn2 || !pn3)
		return NULL;

	    (void)js_GetToken(cx, ts); /* eat `catch' */

	    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
	    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
	    pn3->pn_atom = ts->token.t_atom;
            pn3->pn_expr = NULL;
#if JS_HAS_CATCH_GUARD
	    if (js_PeekToken(cx, ts) == TOK_COLON) {
		(void)js_GetToken(cx, ts); /* eat `:' */
		pn3->pn_expr = Expr(cx, ts, tc);
		if (!pn3->pn_expr)
		    return NULL;
	    } 
#endif
	    pn2->pn_kid1 = pn3;

	    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);

	    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
	    pn2->pn_kid3 = Statements(cx, ts, tc);
	    if (!pn2->pn_kid3)
		return NULL;
	    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);

	    catchtail = catchtail->pn_kid2 = pn2;
	}
	catchtail->pn_kid2 = NULL;

	if (js_MatchToken(cx, ts, TOK_FINALLY)) {
	    tc->tryCount++;
	    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
	    pn->pn_kid3 = Statements(cx, ts, tc);
	    if (!pn->pn_kid3)
		return NULL;
	    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
	} else {
	    pn->pn_kid3 = NULL;
	}
	if (!pn->pn_kid2 && !pn->pn_kid3) {
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					JSMSG_CATCH_OR_FINALLY);
	    return NULL;
	}
	tc->tryCount++;
	return pn;
      }
      case TOK_THROW:
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
	pn2 = Expr(cx, ts, tc);
	if (!pn2)
	    return NULL;
	pn->pn_pos.end = pn2->pn_pos.end;
	if (pn->pn_pos.end.lineno == ts->lineno &&
	    !WellTerminated(cx, ts, TOK_ERROR)) {
	    return NULL;
	}
	pn->pn_op = JSOP_THROW;
	pn->pn_kid = pn2;
	break;

      /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
      case TOK_CATCH:
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
				    JSMSG_CATCH_WITHOUT_TRY);
	return NULL;

      case TOK_FINALLY:
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
				    JSMSG_FINALLY_WITHOUT_TRY);
	return NULL;

#endif /* JS_HAS_EXCEPTIONS */

      case TOK_BREAK:
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	if (!MatchLabel(cx, ts, pn))
	    return NULL;
	stmt = tc->topStmt;
	label = pn->pn_atom;
	if (label) {
	    for (; ; stmt = stmt->down) {
		if (!stmt) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_LABEL_NOT_FOUND);
		    return NULL;
		}
		if (stmt->type == STMT_LABEL && stmt->label == label)
		    break;
	    }
	} else {
	    for (; ; stmt = stmt->down) {
		if (!stmt) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_TOUGH_BREAK);
		    return NULL;
		}
		if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
		    break;
	    }
	}
	if (label)
	    pn->pn_pos.end = ts->token.pos.end;
	break;

      case TOK_CONTINUE:
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	if (!MatchLabel(cx, ts, pn))
	    return NULL;
	stmt = tc->topStmt;
	label = pn->pn_atom;
	if (label) {
	    for (stmt2 = NULL; ; stmt = stmt->down) {
		if (!stmt) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_LABEL_NOT_FOUND);
		    return NULL;
		}
		if (stmt->type == STMT_LABEL) {
		    if (stmt->label == label) {
			if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
			    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
							JSMSG_BAD_CONTINUE);
			    return NULL;
			}
			break;
		    }
		} else {
		    stmt2 = stmt;
		}
	    }
	} else {
	    for (; ; stmt = stmt->down) {
		if (!stmt) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_BAD_CONTINUE);
		    return NULL;
		}
		if (STMT_IS_LOOP(stmt))
		    break;
	    }
	}
	if (label)
	    pn->pn_pos.end = ts->token.pos.end;
	break;

      case TOK_WITH:
	pn = NewParseNode(cx, &ts->token, PN_BINARY);
	if (!pn)
	    return NULL;
	MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
	pn2 = Expr(cx, ts, tc);
	if (!pn2)
	    return NULL;
	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
	pn->pn_left = pn2;

	js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
	pn2 = Statement(cx, ts, tc);
	if (!pn2)
	    return NULL;
	js_PopStatement(tc);

	pn->pn_pos.end = pn2->pn_pos.end;
	pn->pn_right = pn2;
	return pn;

      case TOK_VAR:
	pn = Variables(cx, ts, tc);
	if (!pn)
	    return NULL;
	if (pn->pn_pos.end.lineno == ts->lineno &&
	    !WellTerminated(cx, ts, TOK_ERROR)) {
	    return NULL;
	}
	/* Tell js_EmitTree to generate a final POP. */
	pn->pn_op = JSOP_POP;
	break;

      case TOK_RETURN:
	if (!(tc->flags & TCF_IN_FUNCTION)) {
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					JSMSG_BAD_RETURN);
	    return NULL;
	}
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;

	/* This is ugly, but we don't want to require a semicolon. */
	ts->flags |= TSF_REGEXP;
	tt = js_PeekTokenSameLine(cx, ts);
	ts->flags &= ~TSF_REGEXP;
	if (tt == TOK_ERROR)
	    return NULL;

	if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
	    pn2 = Expr(cx, ts, tc);
	    if (!pn2)
		return NULL;
	    if (pn2->pn_pos.end.lineno == ts->lineno &&
		!WellTerminated(cx, ts, TOK_ERROR)) {
		return NULL;
	    }
	    tc->flags |= TCF_RETURN_EXPR;
	    pn->pn_pos.end = pn2->pn_pos.end;
	    pn->pn_kid = pn2;
	} else {
	    tc->flags |= TCF_RETURN_VOID;
	    pn->pn_kid = NULL;
	}

#ifdef CHECK_RETURN_EXPR
	if ((tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) ==
	    (TCF_RETURN_EXPR | TCF_RETURN_VOID)) {
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					JSMSG_NO_RETURN_VALUE);
	    return NULL;
	}
#endif
	break;

      case TOK_LC:
	js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
	pn = Statements(cx, ts, tc);
	if (!pn)
	    return NULL;

	MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
	js_PopStatement(tc);
	return pn;

      case TOK_EOL:
      case TOK_SEMI:
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
	pn->pn_type = TOK_SEMI;
	pn->pn_kid = NULL;
	return pn;

#if JS_HAS_DEBUGGER_KEYWORD
      case TOK_DEBUGGER:
	if(!WellTerminated(cx, ts, TOK_ERROR))
	    return NULL;
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	pn->pn_type = TOK_DEBUGGER;
	return pn;
#endif /* JS_HAS_DEBUGGER_KEYWORD */

      case TOK_ERROR:
	return NULL;

      default:
	lastExprType = ts->token.type;
	js_UngetToken(ts);
	pn2 = Expr(cx, ts, tc);
	if (!pn2)
	    return NULL;

	tt = ts->pushback.type;
	if (tt == TOK_COLON) {
	    if (pn2->pn_type != TOK_NAME) {
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					    JSMSG_BAD_LABEL);
		return NULL;
	    }
	    label = pn2->pn_atom;
	    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
		if (stmt->type == STMT_LABEL && stmt->label == label) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_DUPLICATE_LABEL);
		    return NULL;
		}
	    }
	    js_GetToken(cx, ts);

	    /* Push a label struct and parse the statement. */
	    js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
	    stmtInfo.label = label;
	    pn = Statement(cx, ts, tc);
	    if (!pn)
		return NULL;

	    /* Pop the label, set pn_expr, and return early. */
	    js_PopStatement(tc);
	    pn2->pn_type = TOK_COLON;
	    pn2->pn_pos.end = pn->pn_pos.end;
	    pn2->pn_expr = pn;
	    return pn2;
	}

	/* Check explicity against (multi-line) function statement */
	if (pn2->pn_pos.end.lineno == ts->lineno &&
	    !WellTerminated(cx, ts, lastExprType)) {
	    return NULL;
	}
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
	pn->pn_type = TOK_SEMI;
	pn->pn_pos = pn2->pn_pos;
	pn->pn_kid = pn2;
	break;
    }

    (void) js_MatchToken(cx, ts, TOK_SEMI);
    return pn;
}

static JSParseNode *
Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn2;
    JSObject *obj, *pobj;
    JSFunction *fun;
    JSClass *clasp;
    JSPropertyOp getter, setter, currentGetter, currentSetter;
    JSAtom *atom;
    JSProperty *prop;
    JSScopeProperty *sprop;
    JSBool ok;

    /*
     * The tricky part of this code is to create special
     * parsenode opcodes for getting and setting variables
     * (which will be stored as special slots in the frame).
     * The complex special case is an eval() inside a
     * function. If the evaluated string references variables in
     * the enclosing function, then we need to generate
     * the special variable opcodes.
     * We determine this by looking up the variable id in the
     * current variable scope.
     */
    JS_ASSERT(ts->token.type == TOK_VAR);
    pn = NewParseNode(cx, &ts->token, PN_LIST);
    if (!pn)
	return NULL;
    pn->pn_op = JSOP_NOP;
    PN_INIT_LIST(pn);

    obj = js_FindVariableScope(cx, &fun);
    if (!obj)
	return NULL;
    clasp = OBJ_GET_CLASS(cx, obj);
    if (fun && clasp == &js_FunctionClass) {
	/* We are compiling code inside a function */
	getter = js_GetLocalVariable;
	setter = js_SetLocalVariable;
    } else if (fun && clasp == &js_CallClass) {
	/* We are compiling code from an eval inside a function */
	getter = js_GetCallVariable;
	setter = js_SetCallVariable;
    } else {
	getter = clasp->getProperty;
	setter = clasp->setProperty;
    }

    do {
        currentGetter = getter;
        currentSetter = setter;
	MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
	atom = ts->token.t_atom;

	pn2 = NewParseNode(cx, &ts->token, PN_NAME);
	if (!pn2)
	    return NULL;
	pn2->pn_op = JSOP_NAME;
	pn2->pn_atom = atom;
	pn2->pn_expr = NULL;
	pn2->pn_slot = -1;
	PN_APPEND(pn, pn2);

	if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
	    return NULL;
	if (pobj == obj &&
	    OBJ_IS_NATIVE(pobj) &&
	    (sprop = (JSScopeProperty *)prop) != NULL) {
	    if (sprop->getter == js_GetArgument) {
		currentGetter = sprop->getter;
#ifdef CHECK_ARGUMENT_HIDING
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_WARNING,
					    JSMSG_VAR_HIDES_ARG,
					    ATOM_BYTES(atom));
		ok = JS_FALSE;
#else
		ok = JS_TRUE;
#endif
	    } else {
		ok = JS_TRUE;
		if (fun) {
		    /* Not an argument, must be a redeclared local var. */
		    if (clasp == &js_FunctionClass) {
			JS_ASSERT(sprop->getter == js_GetLocalVariable);
			JS_ASSERT(JSVAL_IS_INT(sprop->id) &&
				  JSVAL_TO_INT(sprop->id) < fun->nvars);
		    } else if (clasp == &js_CallClass) {
			if (sprop->getter == js_GetCallVariable) {
			    /*
			     * Referencing a variable introduced by a var
			     * statement in the enclosing function. Check
			     * that the slot number we have is in range.
			     */
			    JS_ASSERT(JSVAL_IS_INT(sprop->id) &&
				      JSVAL_TO_INT(sprop->id) < fun->nvars);
			} else {
			    /*
			     * A variable introduced through another eval:
			     * don't use the special getters and setters
			     * since we can't allocate a slot in the frame.
			     */
			    currentGetter = sprop->getter;
			    currentSetter = sprop->setter;
			}
		    }
		} else {
		    /* Global var: (re-)set id a la js_DefineProperty. */
		    sprop->id = ATOM_KEY(atom);
		}
		sprop->getter = currentGetter;
		sprop->setter = currentSetter;
		sprop->attrs |= JSPROP_ENUMERATE | JSPROP_PERMANENT;
		sprop->attrs &= ~JSPROP_READONLY;
	    }
	} else {
	    /*
	     * Property not found in current variable scope: we have not
	     * seen this variable before.
	     * Define a new variable by adding a property to the current
	     * scope, or by allocating more slots in the function's frame.
	     */
	    sprop = NULL;
	    if (prop) {
		OBJ_DROP_PROPERTY(cx, pobj, prop);
		prop = NULL;
	    }
	    if (currentGetter == js_GetCallVariable) {
		/* Can't increase fun->nvars in an active frame! */
		currentGetter = clasp->getProperty;
		currentSetter = clasp->setProperty;
	    }
	    ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID,
				     currentGetter, currentSetter,
				     JSPROP_ENUMERATE | JSPROP_PERMANENT,
				     &prop);
	    if (ok && prop) {
		pobj = obj;
		if (currentGetter == js_GetLocalVariable) {
		    /*
		     * Allocate more room for variables in the
		     * function's frame. We can do this only
		     * before the function is called.
		     */
		    sprop = (JSScopeProperty *)prop;
		    sprop->id = INT_TO_JSVAL(fun->nvars++);
		}
	    }
	}

	if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
	    if (ts->token.t_op != JSOP_NOP) {
		js_ReportCompileErrorNumber(cx, ts,JSREPORT_ERROR,
					    JSMSG_BAD_VAR_INIT);
		ok = JS_FALSE;
	    } else {
		pn2->pn_expr = AssignExpr(cx, ts, tc);
		if (pn2->pn_expr)
		    pn2->pn_op = JSOP_SETNAME2;
		else
		    ok = JS_FALSE;
	    }
	}

	if (ok && fun && (clasp == &js_FunctionClass ||
			  clasp == &js_CallClass) &&
	    !InWithStatement(tc))
	{
	    /* Depending on the value of the getter, change the
	     * opcodes to the forms for arguments and variables.
	     */
	    if (currentGetter == js_GetArgument) {
		JS_ASSERT(sprop && JSVAL_IS_INT(sprop->id));
		pn2->pn_op = (pn2->pn_op == JSOP_NAME)
			     ? JSOP_GETARG
			     : JSOP_SETARG;
		pn2->pn_slot = JSVAL_TO_INT(sprop->id);
	    } else if (currentGetter == js_GetLocalVariable ||
		       currentGetter == js_GetCallVariable)
	    {
		JS_ASSERT(sprop && JSVAL_IS_INT(sprop->id));
		pn2->pn_op = (pn2->pn_op == JSOP_NAME)
			     ? JSOP_GETVAR
			     : JSOP_SETVAR;
		pn2->pn_slot = JSVAL_TO_INT(sprop->id);
	    }
	}

	if (prop)
	    OBJ_DROP_PROPERTY(cx, pobj, prop);
	if (!ok)
	    return NULL;
    } while (js_MatchToken(cx, ts, TOK_COMMA));

    pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
    return pn;
}

static JSParseNode *
Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn2;

    pn = AssignExpr(cx, ts, tc);
    if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
	pn2 = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn2)
	    return NULL;
	pn2->pn_pos.begin = pn->pn_pos.begin;
	PN_INIT_LIST_1(pn2, pn);
	pn = pn2;
	do {
	    pn2 = AssignExpr(cx, ts, tc);
	    if (!pn2)
		return NULL;
	    PN_APPEND(pn, pn2);
	} while (js_MatchToken(cx, ts, TOK_COMMA));
	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
    }
    return pn;
}

/* ZZZbe don't create functions till codegen? or at least don't bind
 * fn name */
static JSBool
LookupArgOrVar(JSContext *cx, JSAtom *atom, JSTreeContext *tc,
	       JSOp *opp, jsint *slotp)
{
    JSObject *obj, *pobj;
    JSClass *clasp;
    JSFunction *fun;
    JSScopeProperty *sprop;

    obj = js_FindVariableScope(cx, &fun);
    clasp = OBJ_GET_CLASS(cx, obj);
    if (clasp != &js_FunctionClass && clasp != &js_CallClass)
	return JS_TRUE;
    if (InWithStatement(tc))
	return JS_TRUE;
    if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop))
	return JS_FALSE;
    *opp = JSOP_NAME;
    *slotp = -1;
    if (sprop) {
	if (sprop->getter == js_GetArgument) {
	    *opp = JSOP_GETARG;
	    *slotp = JSVAL_TO_INT(sprop->id);
	} else if (sprop->getter == js_GetLocalVariable ||
		   sprop->getter == js_GetCallVariable)
	{
	    *opp = JSOP_GETVAR;
	    *slotp = JSVAL_TO_INT(sprop->id);
	}
	OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
    }
    return JS_TRUE;
}

static JSParseNode *
AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn2;
    JSOp op;

    pn = CondExpr(cx, ts, tc);
    if (pn && js_MatchToken(cx, ts, TOK_ASSIGN)) {
	op = ts->token.t_op;
	for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
	    ;
	switch (pn2->pn_type) {
	  case TOK_NAME:
	    if (pn2->pn_slot >= 0) {
		JS_ASSERT(pn2->pn_op == JSOP_GETARG || pn2->pn_op == JSOP_GETVAR);
		if (pn2->pn_op == JSOP_GETARG)
		    pn2->pn_op = JSOP_SETARG;
		else
		    pn2->pn_op = JSOP_SETVAR;
	    } else {
		pn2->pn_op = JSOP_SETNAME2;
	    }
	    break;
	  case TOK_DOT:
	    pn2->pn_op = JSOP_SETPROP;
	    break;
	  case TOK_LB:
	    pn2->pn_op = JSOP_SETELEM;
	    break;
	  default:
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					JSMSG_BAD_LEFTSIDE_OF_ASS);
	    return NULL;
	}
	pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc));
    }
    return pn;
}

static JSParseNode *
CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn, *pn1, *pn2, *pn3;
#if JS_HAS_IN_OPERATOR
    uintN oldflags;
#endif /* JS_HAS_IN_OPERATOR */

    pn = OrExpr(cx, ts, tc);
    if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
	pn1 = pn;
	pn = NewParseNode(cx, &ts->token, PN_TERNARY);
	if (!pn)
	    return NULL;
#if JS_HAS_IN_OPERATOR
	/*
	 * Always accept the 'in' operator in the middle clause of a ternary,
	 * where it's unambiguous, even if we might be parsing the init of a
	 * for statement.
	 */
	oldflags = tc->flags;
	tc->flags &= ~TCF_IN_FOR_INIT;
#endif /* JS_HAS_IN_OPERATOR */
	pn2 = AssignExpr(cx, ts, tc);
#if JS_HAS_IN_OPERATOR
	tc->flags = oldflags;
#endif /* JS_HAS_IN_OPERATOR */

	if (!pn2)
	    return NULL;
	MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
	pn3 = AssignExpr(cx, ts, tc);
	if (!pn3)
	    return NULL;
	pn->pn_pos.begin = pn1->pn_pos.begin;
	pn->pn_pos.end = pn3->pn_pos.end;
	pn->pn_kid1 = pn1;
	pn->pn_kid2 = pn2;
	pn->pn_kid3 = pn3;
    }
    return pn;
}

static JSParseNode *
OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;

    pn = AndExpr(cx, ts, tc);
    if (pn && js_MatchToken(cx, ts, TOK_OR))
	pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc));
    return pn;
}

static JSParseNode *
AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;

    pn = BitOrExpr(cx, ts, tc);
    if (pn && js_MatchToken(cx, ts, TOK_AND))
	pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc));
    return pn;
}

static JSParseNode *
BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;

    pn = BitXorExpr(cx, ts, tc);
    while (pn && js_MatchToken(cx, ts, TOK_BITOR))
	pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc));
    return pn;
}

static JSParseNode *
BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;

    pn = BitAndExpr(cx, ts, tc);
    while (pn && js_MatchToken(cx, ts, TOK_BITXOR))
	pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc));
    return pn;
}

static JSParseNode *
BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;

    pn = EqExpr(cx, ts, tc);
    while (pn && js_MatchToken(cx, ts, TOK_BITAND))
	pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc));
    return pn;
}

static JSParseNode *
EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;
    JSOp op;

    pn = RelExpr(cx, ts, tc);
    while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
	op = ts->token.t_op;
	pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc));
    }
    return pn;
}

static JSParseNode *
RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;
    JSTokenType tt;
    JSOp op;
#if JS_HAS_IN_OPERATOR
    uintN inForInitFlag;

    inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
    /*
     * Uses of the in operator in ShiftExprs are always unambiguous,
     * so unset the flag that prohibits recognizing it.
     */
    tc->flags &= ~TCF_IN_FOR_INIT;
#endif /* JS_HAS_IN_OPERATOR */

    pn = ShiftExpr(cx, ts, tc);
    while (pn &&
	   (js_MatchToken(cx, ts, TOK_RELOP)
#if JS_HAS_IN_OPERATOR
	    /*
	     * Only recognize the 'in' token as an operator if we're not
	     * currently in the init expr of a for loop.
	     */
	    || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
#endif /* JS_HAS_IN_OPERATOR */
#if JS_HAS_INSTANCEOF
	    || js_MatchToken(cx, ts, TOK_INSTANCEOF)
#endif /* JS_HAS_INSTANCEOF */
	    )) {
	tt = ts->token.type;
	op = ts->token.t_op;
	pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc));
    }
#if JS_HAS_IN_OPERATOR
    /* Restore previous state of inForInit flag. */
    tc->flags |= inForInitFlag;
#endif /* JS_HAS_IN_OPERATOR */

    return pn;
}

static JSParseNode *
ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;
    JSOp op;

    pn = AddExpr(cx, ts, tc);
    while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
	op = ts->token.t_op;
	pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc));
    }
    return pn;
}

static JSParseNode *
AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;
    JSTokenType tt;
    JSOp op;

    pn = MulExpr(cx, ts, tc);
    while (pn &&
	   (js_MatchToken(cx, ts, TOK_PLUS) ||
	    js_MatchToken(cx, ts, TOK_MINUS))) {
	tt = ts->token.type;
	op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
	pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc));
    }
    return pn;
}

static JSParseNode *
MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSParseNode *pn;
    JSTokenType tt;
    JSOp op;

    pn = UnaryExpr(cx, ts, tc);
    while (pn &&
	   (js_MatchToken(cx, ts, TOK_STAR) ||
	    js_MatchToken(cx, ts, TOK_DIVOP))) {
	tt = ts->token.type;
	op = ts->token.t_op;
	pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc));
    }
    return pn;
}

static JSParseNode *
SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
	   const char *name)
{
    while (kid->pn_type == TOK_RP)
	kid = kid->pn_kid;
    if (kid->pn_type != TOK_NAME &&
	kid->pn_type != TOK_DOT &&
	kid->pn_type != TOK_LB) {
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
				    JSMSG_BAD_OPERAND, name);
	return NULL;
    }
    pn->pn_kid = kid;
    return kid;
}

static const char *incop_name_str[] = {"increment", "decrement"};

static JSBool
SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
	    JSParseNode *pn, JSParseNode *kid,
	    JSTokenType tt, JSBool preorder)
{
    jsint num;
    JSOp op;

    kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
    if (!kid)
	return JS_FALSE;
    num = -1;
    switch (kid->pn_type) {
      case TOK_NAME:
	if (!LookupArgOrVar(cx, kid->pn_atom, tc, &op, &num))
	    return JS_FALSE;
	if (op == JSOP_GETARG) {
	    op = (tt == TOK_INC)
		 ? (preorder ? JSOP_INCARG : JSOP_ARGINC)
		 : (preorder ? JSOP_DECARG : JSOP_ARGDEC);
	} else if (op == JSOP_GETVAR) {
	    op = (tt == TOK_INC)
		 ? (preorder ? JSOP_INCVAR : JSOP_VARINC)
		 : (preorder ? JSOP_DECVAR : JSOP_VARDEC);
	} else {
	    op = (tt == TOK_INC)
		 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
		 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
	}
	break;

      case TOK_DOT:
	op = (tt == TOK_INC)
	     ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
	     : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
	break;

      case TOK_LB:
	op = (tt == TOK_INC)
	     ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
	     : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
	break;

      default:
	JS_ASSERT(0);
    }
    pn->pn_op = op;
    pn->pn_num = num;
    return JS_TRUE;
}

static JSParseNode *
UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSTokenType tt;
    JSParseNode *pn, *pn2;

    ts->flags |= TSF_REGEXP;
    tt = js_GetToken(cx, ts);
    ts->flags &= ~TSF_REGEXP;

    switch (tt) {
      case TOK_UNARYOP:
      case TOK_PLUS:
      case TOK_MINUS:
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
	pn->pn_type = TOK_UNARYOP;	/* PLUS and MINUS are binary */
	pn->pn_op = ts->token.t_op;
	pn2 = UnaryExpr(cx, ts, tc);
	if (!pn2)
	    return NULL;
	pn->pn_pos.end = pn2->pn_pos.end;
	pn->pn_kid = pn2;
	break;

      case TOK_INC:
      case TOK_DEC:
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
	pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
	if (!pn2)
	    return NULL;
	if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
	    return NULL;
	pn->pn_pos.end = pn2->pn_pos.end;
	break;

      case TOK_DELETE:
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
	pn2 = UnaryExpr(cx, ts, tc);
	if (!pn2)
	    return NULL;
	if (!SetLvalKid(cx, ts, pn, pn2, js_delete_str))
	    return NULL;
	pn->pn_pos.end = pn2->pn_pos.end;
	break;

      case TOK_ERROR:
	return NULL;

      default:
	js_UngetToken(ts);
	pn = MemberExpr(cx, ts, tc, JS_TRUE);
	if (!pn)
	    return NULL;

	/* Don't look across a newline boundary for a postfix incop. */
	if (pn->pn_pos.end.lineno == ts->lineno) {
	    tt = js_PeekTokenSameLine(cx, ts);
	    if (tt == TOK_INC || tt == TOK_DEC) {
		(void) js_GetToken(cx, ts);
		pn2 = NewParseNode(cx, &ts->token, PN_UNARY);
		if (!pn2)
		    return NULL;
		if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
		    return NULL;
		pn2->pn_pos.begin = pn->pn_pos.begin;
		pn = pn2;
	    }
	}
	break;
    }
    return pn;
}

static JSParseNode *
ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
	     JSParseNode *listNode)
{
    JSBool matched;

    ts->flags |= TSF_REGEXP;
    matched = js_MatchToken(cx, ts, TOK_RP);
    ts->flags &= ~TSF_REGEXP;
    if (!matched) {
	do {
	    JSParseNode *argNode = AssignExpr(cx, ts, tc);
	    if (!argNode)
		return NULL;
	    PN_APPEND(listNode, argNode);
	} while (js_MatchToken(cx, ts, TOK_COMMA));

	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS);
    }
    return listNode;
}

static JSParseNode *
MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
	   JSBool allowCallSyntax)
{
    JSParseNode *pn, *pn2, *pn3;
    JSTokenType tt;

    /* Check for new expression first. */
    ts->flags |= TSF_REGEXP;
    tt = js_PeekToken(cx, ts);
    ts->flags &= ~TSF_REGEXP;
    if (tt == TOK_NEW) {
	(void) js_GetToken(cx, ts);

	pn = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn)
	    return NULL;
	pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
	if (!pn2)
	    return NULL;
	PN_INIT_LIST_1(pn, pn2);

	if (js_MatchToken(cx, ts, TOK_LP)) {
	    pn = ArgumentList(cx, ts, tc, pn);
	    if (!pn)
		return NULL;
	}
	if (pn->pn_count - 1 >= ARGC_LIMIT) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_TOO_MANY_CON_ARGS);
	    return NULL;
	}
	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
    } else {
	pn = PrimaryExpr(cx, ts, tc);
	if (!pn)
	    return NULL;
    }

    while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
	if (tt == TOK_DOT) {
	    pn2 = NewParseNode(cx, &ts->token, PN_NAME);
	    if (!pn2)
		return NULL;
	    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
	    pn2->pn_pos.begin = pn->pn_pos.begin;
	    pn2->pn_pos.end = ts->token.pos.end;
	    pn2->pn_op = JSOP_GETPROP;
	    pn2->pn_expr = pn;
	    pn2->pn_atom = ts->token.t_atom;
	} else if (tt == TOK_LB) {
	    pn2 = NewParseNode(cx, &ts->token, PN_BINARY);
	    if (!pn2)
		return NULL;
	    pn3 = Expr(cx, ts, tc);
	    if (!pn3)
		return NULL;

	    MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
	    pn2->pn_pos.begin = pn->pn_pos.begin;
	    pn2->pn_pos.end = ts->token.pos.end;

            pn2->pn_op = JSOP_GETELEM;
            pn2->pn_left = pn;
            pn2->pn_right = pn3;
	} else if (allowCallSyntax && tt == TOK_LP) {
	    pn2 = NewParseNode(cx, &ts->token, PN_LIST);
	    if (!pn2)
		return NULL;
	    pn2->pn_op = JSOP_CALL;
	    PN_INIT_LIST_1(pn2, pn);

	    pn2 = ArgumentList(cx, ts, tc, pn2);
	    if (!pn2)
		return NULL;
	    if (pn2->pn_count - 1 >= ARGC_LIMIT) {
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_TOO_MANY_FUN_ARGS);
		return NULL;
	    }
	    pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end;
	} else {
	    js_UngetToken(ts);
	    return pn;
	}

	pn = pn2;
    }
    if (tt == TOK_ERROR)
	return NULL;
    return pn;
}

static JSParseNode *
PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
    JSTokenType tt;
    JSParseNode *pn, *pn2, *pn3;

#if JS_HAS_SHARP_VARS
    JSParseNode *defsharp;
    JSBool notsharp;

    defsharp = NULL;
    notsharp = JS_FALSE;
  again:
    /*
     * Control flows here after #n= is scanned.  If the following primary is
     * not valid after such a "sharp variable" definition, the token type case
     * should set notsharp.
     */
#endif

    ts->flags |= TSF_REGEXP;
    tt = js_GetToken(cx, ts);
    ts->flags &= ~TSF_REGEXP;

    switch (tt) {
#if JS_HAS_LEXICAL_CLOSURE
      case TOK_FUNCTION:
	pn = FunctionExpr(cx, ts, tc);
	if (!pn)
	    return NULL;
	break;
#endif

#if JS_HAS_INITIALIZERS
      case TOK_LB:
      {
	JSBool matched;
	jsint atomIndex;

	pn = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn)
	    return NULL;
	pn->pn_type = TOK_RB;
	pn->pn_extra = JS_FALSE;

#if JS_HAS_SHARP_VARS
	if (defsharp) {
	    PN_INIT_LIST_1(pn, defsharp);
	    defsharp = NULL;
	} else
#endif
	    PN_INIT_LIST(pn);

	ts->flags |= TSF_REGEXP;
	matched = js_MatchToken(cx, ts, TOK_RB);
	ts->flags &= ~TSF_REGEXP;
	if (!matched) {
	    for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {
		ts->flags |= TSF_REGEXP;
		tt = js_PeekToken(cx, ts);
		ts->flags &= ~TSF_REGEXP;
		if (tt == TOK_RB) {
		    pn->pn_extra = JS_TRUE;
		    break;
		}

		if (tt == TOK_COMMA)
		    pn2 = NewParseNode(cx, &ts->token, PN_NULLARY);
		else
		    pn2 = AssignExpr(cx, ts, tc);
		if (!pn2)
		    return NULL;
		PN_APPEND(pn, pn2);

		if (!js_MatchToken(cx, ts, TOK_COMMA))
		    break;
	    }

	    MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
	}
	pn->pn_pos.end = ts->token.pos.end;
	return pn;
      }

      case TOK_LC:
	pn = NewParseNode(cx, &ts->token, PN_LIST);
	if (!pn)
	    return NULL;
	pn->pn_type = TOK_RC;

#if JS_HAS_SHARP_VARS
	if (defsharp) {
	    PN_INIT_LIST_1(pn, defsharp);
	    defsharp = NULL;
	} else
#endif
	    PN_INIT_LIST(pn);

	if (!js_MatchToken(cx, ts, TOK_RC)) {
	    do {
		tt = js_GetToken(cx, ts);
		switch (tt) {
		  case TOK_NUMBER:
		    pn3 = NewParseNode(cx, &ts->token, PN_NULLARY);
		    if (pn3)
			pn3->pn_dval = ts->token.t_dval;
		    break;
		  case TOK_NAME:
		  case TOK_STRING:
		    pn3 = NewParseNode(cx, &ts->token, PN_NULLARY);
		    if (pn3)
			pn3->pn_atom = ts->token.t_atom;
		    break;
		  case TOK_RC:
		    goto end_obj_init;
		  default:
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
						JSMSG_BAD_PROP_ID);
		    return NULL;
		}

		MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_ID);
		pn2 = NewBinary(cx, TOK_COLON, JSOP_INITPROP, pn3,
				AssignExpr(cx, ts, tc));
		if (!pn2)
		    return NULL;
		PN_APPEND(pn, pn2);
	    } while (js_MatchToken(cx, ts, TOK_COMMA));

	    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
	}
      end_obj_init:
	pn->pn_pos.end = ts->token.pos.end;
	return pn;

#if JS_HAS_SHARP_VARS
      case TOK_DEFSHARP:
	if (defsharp)
	    goto badsharp;
	defsharp = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!defsharp)
	    return NULL;
	defsharp->pn_num = (jsint) ts->token.t_dval;
	defsharp->pn_kid = NULL;
	goto again;

      case TOK_USESHARP:
	/* Check for forward/dangling references at runtime, to allow eval. */
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	pn->pn_num = (jsint) ts->token.t_dval;
	notsharp = JS_TRUE;
	break;
#endif /* JS_HAS_SHARP_VARS */
#endif /* JS_HAS_INITIALIZERS */

      case TOK_LP:
      {
#if JS_HAS_IN_OPERATOR
	uintN oldflags;
#endif
	pn = NewParseNode(cx, &ts->token, PN_UNARY);
	if (!pn)
	    return NULL;
#if JS_HAS_IN_OPERATOR
	/*
	 * Always accept the 'in' operator in a parenthesized expression,
	 * where it's unambiguous, even if we might be parsing the init of a
	 * for statement.
	 */
	oldflags = tc->flags;
	tc->flags &= ~TCF_IN_FOR_INIT;
#endif
	pn2 = Expr(cx, ts, tc);
#if JS_HAS_IN_OPERATOR
	tc->flags = oldflags;
#endif
	if (!pn2)
	    return NULL;

	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
	pn->pn_type = TOK_RP;
	pn->pn_pos.end = ts->token.pos.end;
	pn->pn_kid = pn2;
	break;
      }

      case TOK_STRING:
#if JS_HAS_SHARP_VARS
	notsharp = JS_TRUE;
#endif
	/* FALL THROUGH */
      case TOK_NAME:
      case TOK_OBJECT:
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	pn->pn_op = ts->token.t_op;
	pn->pn_atom = ts->token.t_atom;
	pn->pn_slot = -1;
	if (tt == TOK_NAME) {
	    if (!LookupArgOrVar(cx, pn->pn_atom, tc, &pn->pn_op, &pn->pn_slot))
		return NULL;
	    pn->pn_arity = PN_NAME;
	    pn->pn_expr = NULL;
	}
	break;

      case TOK_NUMBER:
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	pn->pn_dval = ts->token.t_dval;
#if JS_HAS_SHARP_VARS
	notsharp = JS_TRUE;
#endif
	break;

      case TOK_PRIMARY:
	pn = NewParseNode(cx, &ts->token, PN_NULLARY);
	if (!pn)
	    return NULL;
	pn->pn_op = ts->token.t_op;
#if JS_HAS_SHARP_VARS
	notsharp = JS_TRUE;
#endif
	break;

#if !JS_HAS_EXPORT_IMPORT
      case TOK_EXPORT:
      case TOK_IMPORT:
#endif
      case TOK_RESERVED:
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR, JSMSG_RESERVED_ID,
				    js_DeflateString(cx, ts->token.ptr,
						     ts->token.pos.end.index -
						     ts->token.pos.begin.index)
				    );
	return NULL;

      case TOK_ERROR:
	/* The scanner or one of its subroutines reported the error. */
	return NULL;

      default:
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
	return NULL;
    }

#if JS_HAS_SHARP_VARS
    if (defsharp) {
	if (notsharp) {
  badsharp:
	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
					JSMSG_BAD_SHARP_VAR_DEF);
	    return NULL;
	}
	defsharp->pn_kid = pn;
	return defsharp;
    }
#endif
    return pn;
}

JSBool
js_FoldConstants(JSContext *cx, JSParseNode *pn)
{
    JSParseNode *pn1=NULL, *pn2=NULL, *pn3=NULL;

    switch (pn->pn_arity) {
      case PN_FUNC:
	if (!js_FoldConstants(cx, pn->pn_body))
	    return JS_FALSE;
	break;

      case PN_LIST:
	for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
	    if (!js_FoldConstants(cx, pn2))
		return JS_FALSE;
	}
	break;

      case PN_TERNARY:
	/* Any kid may be null (e.g. for (;;)). */
	pn1 = pn->pn_kid1;
	pn2 = pn->pn_kid2;
	pn3 = pn->pn_kid3;
	if (pn1 && !js_FoldConstants(cx, pn1))
	    return JS_FALSE;
	if (pn2 && !js_FoldConstants(cx, pn2))
	    return JS_FALSE;
	if (pn3 && !js_FoldConstants(cx, pn3))
	    return JS_FALSE;
	break;

      case PN_BINARY:
	/* First kid may be null (for default case in switch). */
	pn1 = pn->pn_left;
	pn2 = pn->pn_right;
	if (pn1 && !js_FoldConstants(cx, pn1))
	    return JS_FALSE;
	if (!js_FoldConstants(cx, pn2))
	    return JS_FALSE;
	break;

      case PN_UNARY:
	/* Our kid may be null (e.g. return; vs. return e;). */
	pn1 = pn->pn_kid;
	if (pn1 && !js_FoldConstants(cx, pn1))
	    return JS_FALSE;
	break;

      case PN_NAME:
	pn1 = pn->pn_expr;
	if (pn1 && !js_FoldConstants(cx, pn1))
	    return JS_FALSE;
	break;

      case PN_NULLARY:
	break;
    }

    switch (pn->pn_type) {
      case TOK_PLUS:
	if (pn1->pn_type == TOK_STRING && pn2->pn_type == TOK_STRING) {
	    JSString *str1, *str2;
	    size_t length, length1, length2, nbytes;
	    void *mark;
	    jschar *chars;

	    /* Concatenate string constants. */
	    str1 = ATOM_TO_STRING(pn1->pn_atom);
	    str2 = ATOM_TO_STRING(pn2->pn_atom);
	    length1 = str1->length;
	    length2 = str2->length;
	    length = length1 + length2;
	    nbytes = (length + 1) * sizeof(jschar);
	    mark = JS_ARENA_MARK(&cx->tempPool);
	    JS_ARENA_ALLOCATE(chars, &cx->tempPool, nbytes);
	    if (!chars) {
		JS_ReportOutOfMemory(cx);
		return JS_FALSE;
	    }
	    js_strncpy(chars, str1->chars, length1);
	    js_strncpy(chars + length1, str2->chars, length2);
	    chars[length] = 0;
	    pn->pn_atom = js_AtomizeChars(cx, chars, length, 0);
	    if (!pn->pn_atom)
		return JS_FALSE;
	    JS_ARENA_RELEASE(&cx->tempPool, mark);
	    pn->pn_type = TOK_STRING;
	    pn->pn_op = JSOP_STRING;
	    pn->pn_arity = PN_NULLARY;
	    break;
	}
	/* FALL THROUGH */

      case TOK_SHOP:
      case TOK_MINUS:
      case TOK_STAR:
      case TOK_DIVOP:
	if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
	    jsdouble d, d2;
	    int32 i, j;
	    uint32 u;

	    /* Fold two numeric constants. */
	    d = pn1->pn_dval;
	    d2 = pn2->pn_dval;
	    switch (pn->pn_op) {
	      case JSOP_LSH:
	      case JSOP_RSH:
		if (!js_DoubleToECMAInt32(cx, d, &i))
		    return JS_FALSE;
		if (!js_DoubleToECMAInt32(cx, d2, &j))
		    return JS_FALSE;
		j &= 31;
		d = (pn->pn_op == JSOP_LSH) ? i << j : i >> j;
		break;

	      case JSOP_URSH:
		if (!js_DoubleToECMAUint32(cx, d, &u))
		    return JS_FALSE;
		if (!js_DoubleToECMAInt32(cx, d2, &j))
		    return JS_FALSE;
		j &= 31;
		d = u >> j;
		break;

	      case JSOP_ADD:
		d += d2;
		break;

	      case JSOP_SUB:
		d -= d2;
		break;

	      case JSOP_MUL:
		d *= d2;
		break;

	      case JSOP_DIV:
		if (d2 == 0) {
#ifdef XP_PC
		    /* XXX MSVC miscompiles such that (NaN == 0) */
		    if (JSDOUBLE_IS_NaN(d2))
			d = *cx->runtime->jsNaN;
		    else
#endif
		    if (d == 0 || JSDOUBLE_IS_NaN(d))
			d = *cx->runtime->jsNaN;
		    else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
			d = *cx->runtime->jsNegativeInfinity;
		    else
			d = *cx->runtime->jsPositiveInfinity;
		} else {
		    d /= d2;
		}
		break;

	      case JSOP_MOD:
		if (d2 == 0) {
		    d = *cx->runtime->jsNaN;
		} else {
#ifdef XP_PC
		  /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
		  if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
#endif
		    d = fmod(d, d2);
		}
		break;

	      default:;
	    }
	    pn->pn_type = TOK_NUMBER;
	    pn->pn_op = JSOP_NUMBER;
	    pn->pn_arity = PN_NULLARY;
	    pn->pn_dval = d;
	}
	break;

      case TOK_UNARYOP:
	if (pn1->pn_type == TOK_NUMBER) {
	    jsdouble d;
	    int32 i;

	    /* Operate on one numeric constants. */
	    d = pn1->pn_dval;
	    switch (pn->pn_op) {
	      case JSOP_BITNOT:
		if (!js_DoubleToECMAInt32(cx, d, &i))
		    return JS_FALSE;
		d = ~i;
		break;

	      case JSOP_NEG:
#ifdef HPUX
                /* 
                 * Negation of a zero doesn't produce a negative
                 * zero on HPUX. Perform the operation by bit
                 * twiddling.
                 */
                JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
#else
                d = -d;
#endif
		break;

	      case JSOP_POS:
		break;

	      case JSOP_NOT:
		pn->pn_type = TOK_PRIMARY;
		pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
		pn->pn_arity = PN_NULLARY;
		/* FALL THROUGH */

	      default:
		/* Return early to dodge the common TOK_NUMBER code. */
		return JS_TRUE;
	    }
	    pn->pn_type = TOK_NUMBER;
	    pn->pn_op = JSOP_NUMBER;
	    pn->pn_arity = PN_NULLARY;
	    pn->pn_dval = d;
	}
	break;

      default:;
    }

    return JS_TRUE;
}

**** End of jsparse.c. ****

**** Start of jsparse.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsparse_h___
#define jsparse_h___
/*
 * JS parser definitions.
 */
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsscan.h"

JS_BEGIN_EXTERN_C

/*
 * Parsing builds a tree of nodes that directs code generation.  This tree is
 * not a concrete syntax tree in all respects (for example, || and && are left
 * associative, but (A && B && C) translates into the right-associated tree
 * <A && <B && C>> so that code generation can emit a left-associative branch
 * around <B && C> when A is false).  Nodes are labeled by token type, with a
 * JSOp secondary label when needed:
 *
 * Label        Variant     Members
 * -----        -------     -------
 * <Definitions>
 * TOK_FUNCTION func        pn_fun: function, contains arg and var properties
 *                          pn_body: TOK_LC node for function body
 *                            NB: We define or create the function object at
 *                            parse (not emit) time, in order to specialize arg
 *                            and var bytecodes early.
 *                          pn_tryCount: of try statements in function
 *
 * <Statements>
 * TOK_LC       list        pn_head: list of pn_count statements
 * TOK_EXPORT   list        pn_head: list of pn_count TOK_NAMEs or one TOK_STAR
 *                            (which is not a multiply node)
 * TOK_IMPORT   list        pn_head: list of pn_count sub-trees of the form
 *                            a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a.
 *                            Each member is expressed with TOK_DOT or TOK_LB.
 *                            Each sub-tree's root node has a pn_op in the set
 *                            JSOP_IMPORT{ALL,PROP,ELEM}
 * TOK_IF       ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else or null
 * TOK_SWITCH   binary      pn_left: discriminant
 *                          pn_right: list of TOK_CASE nodes, with at most one
 *                            TOK_DEFAULT node
 * TOK_CASE,    binary      pn_left: case expr or null if TOK_DEFAULT
 * TOK_DEFAULT              pn_right: TOK_LC node for this case's statements
 * TOK_WHILE    binary      pn_left: cond, pn_right: body
 * TOK_DO       binary      pn_left: body, pn_right: cond
 * TOK_FOR      binary      pn_left: either
 *                            for/in loop: a binary TOK_IN node with
 *                              pn_left:  TOK_VAR or TOK_NAME to left of 'in'
 *                              pn_right: object expr to right of 'in'
 *                            for(;;) loop: a ternary TOK_RESERVED node with
 *                              pn_kid1:  init expr before first ';'
 *                              pn_kid2:  cond expr before second ';'
 *                              pn_kid3:  update expr after second ';'
 *                              any kid may be null
 *                          pn_right: body
 * TOK_THROW    unary       pn_op: JSOP_THROW, pn_kid: exception
 * TOK_BREAK    name        pn_atom: label or null
 * TOK_CONTINUE name        pn_atom: label or null
 * TOK_WITH     binary      pn_left: head expr, pn_right: body
 * TOK_VAR      list        pn_head: list of pn_count TOK_NAME nodes
 *                          each name node has pn_atom: variable name and
 *                          pn_expr: initializer or null
 * TOK_RETURN   unary       pn_kid: return expr or null
 * TOK_SEMI     unary       pn_kid: expr or null statement
 * TOK_COLON    name        pn_atom: label, pn_expr: labeled statement
 *
 * <Expressions>
 * TOK_COMMA    list        pn_head: list of pn_count comma-separated exprs
 * TOK_ASSIGN   binary      pn_left: lvalue, pn_right: rvalue
 * TOK_HOOK     ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else
 * TOK_OR       binary      pn_left: first in || chain, pn_right: rest of chain
 * TOK_AND      binary      pn_left: first in && chain, pn_right: rest of chain
 * TOK_BITOR    binary      pn_left: left-assoc | expr, pn_right: ^ expr
 * TOK_BITXOR   binary      pn_left: left-assoc ^ expr, pn_right: & expr
 * TOK_BITAND   binary      pn_left: left-assoc & expr, pn_right: EQ expr
 * TOK_EQOP     binary      pn_left: left-assoc EQ expr, pn_right: REL expr
 *                          pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE
 * TOK_RELOP    binary      pn_left: left-assoc REL expr, pn_right: SH expr
 *                          pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE
 * TOK_SHOP     binary      pn_left: left-assoc SH expr, pn_right: ADD expr
 *                          pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH
 * TOK_PLUS,    binary      pn_left: left-assoc ADD expr, pn_right: MUL expr
 * TOK_MINUS                pn_op: JSOP_ADD, JSOP_SUB
 * TOK_STAR,    binary      pn_left: left-assoc MUL expr, pn_right: UNARY expr
 * TOK_DIVOP                pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD
 * TOK_UNARYOP  unary       pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS,
 *                          JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID
 * TOK_INC,     unary       pn_kid: MEMBER expr
 * TOK_DEC                  pn_num: arg or local var slot if non-negative
 * TOK_NEW      list        pn_head: list of ctor, arg1, arg2, ... argN
 *                          pn_count: 1 + N (where N is number of args)
 *                          ctor is a MEMBER expr
 * TOK_DELETE   unary       pn_kid: MEMBER expr
 * TOK_DOT      name        pn_expr: MEMBER expr to left of .
 *                          pn_atom: name to right of .
 * TOK_LB       binary      pn_left: MEMBER expr to left of [
 *                          pn_right: expr between [ and ]
 * TOK_LP       list        pn_head: list of call, arg1, arg2, ... argN
 *                          pn_count: 1 + N (where N is number of args)
 *                          call is a MEMBER expr naming a callable object
 * TOK_RB       list        pn_head: list of pn_count array element exprs
 *                          [,,] holes are represented by TOK_COMMA nodes
 *                          #n=[...] produces TOK_DEFSHARP at head of list
 *                          pn_extra: true if extra comma at end
 * TOK_RC       list        pn_head: list of pn_count TOK_COLON nodes where
 *                          each has pn_left: property id, pn_right: value
 *                          #n={...} produces TOK_DEFSHARP at head of list
 * TOK_DEFSHARP unary       pn_num: jsint value of n in #n=
 *                          pn_kid: null for #n=[...] and #n={...}, primary
 *                          if #n=primary for function, paren, name, object
 *                          literal expressions
 * TOK_USESHARP nullary     pn_num: jsint value of n in #n#
 * TOK_RP       unary       pn_kid: parenthesized expression
 * TOK_NAME,    name        pn_atom: name, string, or object atom
 * TOK_STRING,              pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT
 * TOK_OBJECT               pn_op may be *ARG or *VAR with pn_slot >= 0
 * TOK_NUMBER   dval        pn_dval: double value of numeric literal
 * TOK_PRIMARY  nullary     pn_op: JSOp bytecode
 */
typedef enum JSParseNodeArity {
    PN_FUNC     = -3,
    PN_LIST     = -2,
    PN_TERNARY  =  3,
    PN_BINARY   =  2,
    PN_UNARY    =  1,
    PN_NAME     = -1,
    PN_NULLARY  =  0
} JSParseNodeArity;

struct JSParseNode {
    JSTokenType         pn_type;
    JSTokenPos          pn_pos;
    JSOp                pn_op;
    ptrdiff_t           pn_offset;      /* first generated bytecode offset */
    JSParseNodeArity    pn_arity;
    union {
	struct {                        /* TOK_FUNCTION node */
	    JSFunction  *fun;           /* function object private data */
	    JSParseNode *body;          /* TOK_LC list of statements */
	    uint32      tryCount;       /* try statement count */
	} func;
	struct {                        /* list of next-linked nodes */
	    JSParseNode *head;          /* first node in list */
	    JSParseNode **tail;         /* ptr to ptr to last node in list */
	    uint32      count;          /* number of nodes in list */
	    JSBool      extra;          /* extra comma flag for [1,2,,] */
	} list;
	struct {                        /* ternary: if, for(;;), ?: */
	    JSParseNode *kid1;          /* condition, discriminant, etc. */
	    JSParseNode *kid2;          /* then-part, case list, etc. */
	    JSParseNode *kid3;          /* else-part, default case, etc. */
	} ternary;
	struct {                        /* two kids if binary */
	    JSParseNode *left;
	    JSParseNode *right;
	    jsval       val;            /* switch case value */
	} binary;
	struct {                        /* one kid if unary */
	    JSParseNode *kid;
	    jsint       num;            /* -1 or arg or local/sharp var num */
	} unary;
	struct {                        /* name, labeled statement, etc. */
	    JSAtom      *atom;          /* name or label atom, null if slot */
	    JSParseNode *expr;          /* object or initializer */
	    jsint       slot;           /* -1 or arg or local var slot */
	} name;
	jsdouble        dval;           /* aligned numeric literal value */
    } pn_u;
    JSParseNode         *pn_next;       /* here to align dval and pn_u on RISCs */
};

#define pn_fun          pn_u.func.fun
#define pn_body         pn_u.func.body
#define pn_tryCount     pn_u.func.tryCount
#define pn_head         pn_u.list.head
#define pn_tail         pn_u.list.tail
#define pn_count        pn_u.list.count
#define pn_extra        pn_u.list.extra
#define pn_kid1         pn_u.ternary.kid1
#define pn_kid2         pn_u.ternary.kid2
#define pn_kid3         pn_u.ternary.kid3
#define pn_left         pn_u.binary.left
#define pn_right        pn_u.binary.right
#define pn_val          pn_u.binary.val
#define pn_kid          pn_u.unary.kid
#define pn_num          pn_u.unary.num
#define pn_atom         pn_u.name.atom
#define pn_expr         pn_u.name.expr
#define pn_slot         pn_u.name.slot
#define pn_dval         pn_u.dval

/*
 * Compute a pointer to the last JSParseNode element in a singly-linked list.
 * NB: list must be non-empty for correct PN_LAST usage!
 */
#define PN_LAST(list) \
    ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next)))

#define PN_INIT_LIST(list)                                                    \
    JS_BEGIN_MACRO                                                            \
	(list)->pn_head = NULL;                                               \
	(list)->pn_tail = &(list)->pn_head;                                   \
	(list)->pn_count = 0;                                                 \
    JS_END_MACRO

#define PN_INIT_LIST_1(list, pn)                                              \
    JS_BEGIN_MACRO                                                            \
	(list)->pn_head = (pn);                                               \
	(list)->pn_tail = &(pn)->pn_next;                                     \
	(list)->pn_count = 1;                                                 \
    JS_END_MACRO

#define PN_APPEND(list, pn)                                                   \
    JS_BEGIN_MACRO                                                            \
	*(list)->pn_tail = (pn);                                              \
	(list)->pn_tail = &(pn)->pn_next;                                     \
	(list)->pn_count++;                                                   \
    JS_END_MACRO

/* New names to connote code generation in addition to parse tree gen. */
#define js_CompileTokenStream   js_Parse
#define js_CompileFunctionBody  js_ParseFunctionBody

extern JS_FRIEND_API(JSBool)
js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
		      JSCodeGenerator *cg);

extern JSBool
js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun);

/* XXXbe expose js_Parse{TokenStream,FunctionBody} that return trees? */
extern JSBool
js_FoldConstants(JSContext *cx, JSParseNode *pn);

JS_END_EXTERN_C

#endif /* jsparse_h___ */

**** End of jsparse.h. ****

**** Start of jsprf.c. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
** Portable safe sprintf code.
**
** Author: Kipp E.B. Hickman
*/
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "jsprf.h"
#include "jslong.h"
#include "jsutil.h" /* Added by JSIFY */

/*
** Note: on some platforms va_list is defined as an array,
** and requires array notation.
*/
#if (defined(linux) && defined(__powerpc__)) || defined(WIN16)
#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
#else
#define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
#endif /* Linux/PPC || WIN16 */

/*
** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it)
*/

/*
** XXX This needs to be internationalized!
*/

typedef struct SprintfStateStr SprintfState;

struct SprintfStateStr {
    int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len);

    char *base;
    char *cur;
    JSUint32 maxlen;

    int (*func)(void *arg, const char *sp, JSUint32 len);
    void *arg;
};

/*
** Numbered Arguement State
*/
struct NumArgState{
    int	    type;		/* type of the current ap                    */
    va_list ap;			/* point to the corresponding position on ap */
};

static JSBool  l10n_debug_init = JS_FALSE;
static JSBool  l10n_debug = JS_FALSE;

#define NAS_DEFAULT_NUM 20  /* default number of NumberedArgumentState array */


#define TYPE_INT16	0
#define TYPE_UINT16	1
#define TYPE_INTN	2
#define TYPE_UINTN	3
#define TYPE_INT32	4
#define TYPE_UINT32	5
#define TYPE_INT64	6
#define TYPE_UINT64	7
#define TYPE_STRING	8
#define TYPE_DOUBLE	9
#define TYPE_INTSTR	10
#define TYPE_UNKNOWN	20

#define _LEFT		0x1
#define _SIGNED		0x2
#define _SPACED		0x4
#define _ZEROS		0x8
#define _NEG		0x10

/*
** Fill into the buffer using the data in src
*/
static int fill2(SprintfState *ss, const char *src, int srclen, int width,
		int flags)
{
    char space = ' ';
    int rv;

    width -= srclen;
    if ((width > 0) && ((flags & _LEFT) == 0)) {	/* Right adjusting */
	if (flags & _ZEROS) {
	    space = '0';
	}
	while (--width >= 0) {
	    rv = (*ss->stuff)(ss, &space, 1);
	    if (rv < 0) {
		return rv;
	    }
	}
    }

    /* Copy out the source data */
    rv = (*ss->stuff)(ss, src, srclen);
    if (rv < 0) {
	return rv;
    }

    if ((width > 0) && ((flags & _LEFT) != 0)) {	/* Left adjusting */
	while (--width >= 0) {
	    rv = (*ss->stuff)(ss, &space, 1);
	    if (rv < 0) {
		return rv;
	    }
	}
    }
    return 0;
}

/*
** Fill a number. The order is: optional-sign zero-filling conversion-digits
*/
static int fill_n(SprintfState *ss, const char *src, int srclen, int width,
		  int prec, int type, int flags)
{
    int zerowidth = 0;
    int precwidth = 0;
    int signwidth = 0;
    int leftspaces = 0;
    int rightspaces = 0;
    int cvtwidth;
    int rv;
    char sign;

    if ((type & 1) == 0) {
	if (flags & _NEG) {
	    sign = '-';
	    signwidth = 1;
	} else if (flags & _SIGNED) {
	    sign = '+';
	    signwidth = 1;
	} else if (flags & _SPACED) {
	    sign = ' ';
	    signwidth = 1;
	}
    }
    cvtwidth = signwidth + srclen;

    if (prec > 0) {
	if (prec > srclen) {
	    precwidth = prec - srclen;		/* Need zero filling */
	    cvtwidth += precwidth;
	}
    }

    if ((flags & _ZEROS) && (prec < 0)) {
	if (width > cvtwidth) {
	    zerowidth = width - cvtwidth;	/* Zero filling */
	    cvtwidth += zerowidth;
	}
    }

    if (flags & _LEFT) {
	if (width > cvtwidth) {
	    /* Space filling on the right (i.e. left adjusting) */
	    rightspaces = width - cvtwidth;
	}
    } else {
	if (width > cvtwidth) {
	    /* Space filling on the left (i.e. right adjusting) */
	    leftspaces = width - cvtwidth;
	}
    }
    while (--leftspaces >= 0) {
	rv = (*ss->stuff)(ss, " ", 1);
	if (rv < 0) {
	    return rv;
	}
    }
    if (signwidth) {
	rv = (*ss->stuff)(ss, &sign, 1);
	if (rv < 0) {
	    return rv;
	}
    }
    while (--precwidth >= 0) {
	rv = (*ss->stuff)(ss, "0", 1);
	if (rv < 0) {
	    return rv;
	}
    }
    while (--zerowidth >= 0) {
	rv = (*ss->stuff)(ss, "0", 1);
	if (rv < 0) {
	    return rv;
	}
    }
    rv = (*ss->stuff)(ss, src, srclen);
    if (rv < 0) {
	return rv;
    }
    while (--rightspaces >= 0) {
	rv = (*ss->stuff)(ss, " ", 1);
	if (rv < 0) {
	    return rv;
	}
    }
    return 0;
}

/*
** Convert a long into its printable form
*/
static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
		 int type, int flags, const char *hexp)
{
    char cvtbuf[100];
    char *cvt;
    int digits;

    /* according to the man page this needs to happen */
    if ((prec == 0) && (num == 0)) {
	return 0;
    }

    /*
    ** Converting decimal is a little tricky. In the unsigned case we
    ** need to stop when we hit 10 digits. In the signed case, we can
    ** stop when the number is zero.
    */
    cvt = cvtbuf + sizeof(cvtbuf);
    digits = 0;
    while (num) {
	int digit = (((unsigned long)num) % radix) & 0xF;
	*--cvt = hexp[digit];
	digits++;
	num = (long)(((unsigned long)num) / radix);
    }
    if (digits == 0) {
	*--cvt = '0';
	digits++;
    }

    /*
    ** Now that we have the number converted without its sign, deal with
    ** the sign and zero padding.
    */
    return fill_n(ss, cvt, digits, width, prec, type, flags);
}

/*
** Convert a 64-bit integer into its printable form
*/
static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix,
		  int type, int flags, const char *hexp)
{
    char cvtbuf[100];
    char *cvt;
    int digits;
    JSInt64 rad;

    /* according to the man page this needs to happen */
    if ((prec == 0) && (JSLL_IS_ZERO(num))) {
	return 0;
    }

    /*
    ** Converting decimal is a little tricky. In the unsigned case we
    ** need to stop when we hit 10 digits. In the signed case, we can
    ** stop when the number is zero.
    */
    JSLL_I2L(rad, radix);
    cvt = cvtbuf + sizeof(cvtbuf);
    digits = 0;
    while (!JSLL_IS_ZERO(num)) {
	JSInt32 digit;
	JSInt64 quot, rem;
	JSLL_UDIVMOD(&quot, &rem, num, rad);
	JSLL_L2I(digit, rem);
	*--cvt = hexp[digit & 0xf];
	digits++;
	num = quot;
    }
    if (digits == 0) {
	*--cvt = '0';
	digits++;
    }

    /*
    ** Now that we have the number converted without its sign, deal with
    ** the sign and zero padding.
    */
    return fill_n(ss, cvt, digits, width, prec, type, flags);
}

/*
** Convert a double precision floating point number into its printable
** form.
**
** XXX stop using sprintf to convert floating point
*/
static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
{
    char fin[20];
    char fout[300];
    int amount = fmt1 - fmt0;

    JS_ASSERT((amount > 0) && (amount < sizeof(fin)));
    if (amount >= sizeof(fin)) {
	/* Totally bogus % command to sprintf. Just ignore it */
	return 0;
    }
    memcpy(fin, fmt0, amount);
    fin[amount] = 0;

    /* Convert floating point using the native sprintf code */
#ifdef DEBUG
    {
        const char *p = fin;
        while (*p) {
            JS_ASSERT(*p != 'L');
            p++;
        }
    }
#endif
    sprintf(fout, fin, d);

    /*
    ** This assert will catch overflow's of fout, when building with
    ** debugging on. At least this way we can track down the evil piece
    ** of calling code and fix it!
    */
    JS_ASSERT(strlen(fout) < sizeof(fout));

    return (*ss->stuff)(ss, fout, strlen(fout));
}

/*
** Convert a string into its printable form.  "width" is the output
** width. "prec" is the maximum number of characters of "s" to output,
** where -1 means until NUL.
*/
static int cvt_s(SprintfState *ss, const char *s, int width, int prec,
		 int flags)
{
    int slen;

    if (prec == 0)
	return 0;

    /* Limit string length by precision value */
    slen = s ? strlen(s) : 6;
    if (prec > 0) {
	if (prec < slen) {
	    slen = prec;
	}
    }

    /* and away we go */
    return fill2(ss, s ? s : "(null)", slen, width, flags);
}

/*
** BiuldArgArray stands for Numbered Argument list Sprintf
** for example,  
**	fmp = "%4$i, %2$d, %3s, %1d";
** the number must start from 1, and no gap among them
*/

static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )
{
    int number = 0, cn = 0, i;
    const char* p;
    char  c;
    struct NumArgState* nas;
    

    /*
    ** set the l10n_debug flag
    ** this routine should be executed only once
    ** 'cause getenv does take time
    */
    if( !l10n_debug_init ){
	l10n_debug_init = JS_TRUE;
	p = getenv( "NETSCAPE_LOCALIZATION_DEBUG" );
	if( ( p != NULL ) && ( *p == '1' ) ){
	    l10n_debug = JS_TRUE;
	}
    }


    /*
    **	first pass:
    **	detemine how many legal % I have got, then allocate space
    */

    p = fmt;
    *rv = 0;
    i = 0;
    while( ( c = *p++ ) != 0 ){
	if( c != '%' )
	    continue;
	if( ( c = *p++ ) == '%' )	/* skip %% case */
	    continue;

	while( c != 0 ){
	    if( c > '9' || c < '0' ){
		if( c == '$' ){		/* numbered argument csae */
		    if( i > 0 ){
			*rv = -1;
			if( l10n_debug )
			    printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );
			return NULL;
		    }
		    number++;
		    break;

		} else{			/* non-numbered argument case */
		    if( number > 0 ){
			if( l10n_debug )
			    printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );
			*rv = -1;
			return NULL;
		    }
		    i = 1;
		    break;
		}
	    }

	    c = *p++;
	}
    }

    if( number == 0 ){
	return NULL;
    }

    
    if( number > NAS_DEFAULT_NUM ){
	nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) );
	if( !nas ){
	    *rv = -1;
	    if( l10n_debug )
		printf( "malloc() error for \"%s\"\n", fmt );
	    return NULL;
	}
    } else {
	nas = nasArray;
    }

    for( i = 0; i < number; i++ ){
	nas[i].type = TYPE_UNKNOWN;
    }


    /*
    ** second pass:
    ** set nas[].type
    */

    p = fmt;
    while( ( c = *p++ ) != 0 ){
    	if( c != '%' )	continue;
	    c = *p++;
	if( c == '%' )	continue;

	cn = 0;
	while( c && c != '$' ){	    /* should imporve error check later */
	    cn = cn*10 + c - '0';
	    c = *p++;
	}

	if( !c || cn < 1 || cn > number ){
	    *rv = -1;
	    if( l10n_debug )
		printf( "invalid argument number (valid range [1, %d]), \"%s\"\n", number, fmt );
	    break;
        }

	/* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */
        cn--;
	if( nas[cn].type != TYPE_UNKNOWN )
	    continue;

        c = *p++;

        /* width */
        if (c == '*') {
	    /* not supported feature, for the argument is not numbered */
	    *rv = -1;
	    if( l10n_debug )
		printf( "* width specifier not support for numbered arguments \"%s\"\n", fmt );
	    break;
	} else {
	    while ((c >= '0') && (c <= '9')) {
	        c = *p++;
	    }
	}

	/* precision */
	if (c == '.') {
	    c = *p++;
	    if (c == '*') {
	        /* not supported feature, for the argument is not numbered */
		if( l10n_debug )
		    printf( "* precision specifier not support for numbered arguments \"%s\"\n", fmt );
	        *rv = -1;
	        break;
	    } else {
	        while ((c >= '0') && (c <= '9')) {
		    c = *p++;
		}
	    }
	}

	/* size */
	nas[cn].type = TYPE_INTN;
	if (c == 'h') {
	    nas[cn].type = TYPE_INT16;
	    c = *p++;
	} else if (c == 'L') {
	    /* XXX not quite sure here */
	    nas[cn].type = TYPE_INT64;
	    c = *p++;
	} else if (c == 'l') {
	    nas[cn].type = TYPE_INT32;
	    c = *p++;
	    if (c == 'l') {
	        nas[cn].type = TYPE_INT64;
	        c = *p++;
	    }
	}

	/* format */
	switch (c) {
	case 'd':
	case 'c':
	case 'i':
	case 'o':
	case 'u':
	case 'x':
	case 'X':
	    break;

	case 'e':
	case 'f':
	case 'g':
	    nas[ cn ].type = TYPE_DOUBLE;
	    break;

	case 'p':
	    /* XXX should use cpp */
	    if (sizeof(void *) == sizeof(JSInt32)) {
		nas[ cn ].type = TYPE_UINT32;
	    } else if (sizeof(void *) == sizeof(JSInt64)) {
	        nas[ cn ].type = TYPE_UINT64;
	    } else if (sizeof(void *) == sizeof(JSIntn)) {
	        nas[ cn ].type = TYPE_UINTN;
	    } else {
	        nas[ cn ].type = TYPE_UNKNOWN;
	    }
	    break;

	case 'C':
	case 'S':
	case 'E':
	case 'G':
	    /* XXX not supported I suppose */
	    JS_ASSERT(0);
	    nas[ cn ].type = TYPE_UNKNOWN;
	    break;

	case 's':
	    nas[ cn ].type = TYPE_STRING;
	    break;

	case 'n':
	    nas[ cn ].type = TYPE_INTSTR;
	    break;

	default:
	    JS_ASSERT(0);
	    nas[ cn ].type = TYPE_UNKNOWN;
	    break;
	}

	/* get a legal para. */
	if( nas[ cn ].type == TYPE_UNKNOWN ){
	    if( l10n_debug )
		printf( "unknown type \"%s\"\n", fmt );
	    *rv = -1;
	    break;
	}
    }


    /*
    ** third pass
    ** fill the nas[cn].ap
    */

    if( *rv < 0 ){
	if( nas != nasArray )
	    JS_DELETE( nas );
	return NULL;
    }

    cn = 0;
    while( cn < number ){
	if( nas[cn].type == TYPE_UNKNOWN ){
	    cn++;
	    continue;
	}

	VARARGS_ASSIGN(nas[cn].ap, ap);

	switch( nas[cn].type ){
	case TYPE_INT16:
	case TYPE_UINT16:
	case TYPE_INTN:
	case TYPE_UINTN:		(void)va_arg( ap, JSIntn );		break;

	case TYPE_INT32:		(void)va_arg( ap, JSInt32 );		break;

	case TYPE_UINT32:	(void)va_arg( ap, JSUint32 );	break;

	case TYPE_INT64:	(void)va_arg( ap, JSInt64 );		break;

	case TYPE_UINT64:	(void)va_arg( ap, JSUint64 );		break;

	case TYPE_STRING:	(void)va_arg( ap, char* );		break;

	case TYPE_INTSTR:	(void)va_arg( ap, JSIntn* );		break;

	case TYPE_DOUBLE:	(void)va_arg( ap, double );		break;

	default:
	    if( nas != nasArray )
		JS_DELETE( nas );
	    *rv = -1;
	    return NULL;
	}

	cn++;
    }


    return nas;
}

/*
** The workhorse sprintf code.
*/
static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)
{
    char c;
    int flags, width, prec, radix, type;
    union {
	char ch;
	int i;
	long l;
	JSInt64 ll;
	double d;
	const char *s;
	int *ip;
    } u;
    const char *fmt0;
    static char *hex = "0123456789abcdef";
    static char *HEX = "0123456789ABCDEF";
    char *hexp;
    int rv, i;
    struct NumArgState* nas = NULL;
    struct NumArgState  nasArray[ NAS_DEFAULT_NUM ];
    char  pattern[20];
    const char* dolPt = NULL;  /* in "%4$.2f", dolPt will poiont to . */


    /*
    ** build an argument array, IF the fmt is numbered argument
    ** list style, to contain the Numbered Argument list pointers
    */

    nas = BuildArgArray( fmt, ap, &rv, nasArray );
    if( rv < 0 ){
	/* the fmt contains error Numbered Argument format, jliu@netscape.com */
	JS_ASSERT(0);
	return rv;
    }

    while ((c = *fmt++) != 0) {
	if (c != '%') {
	    rv = (*ss->stuff)(ss, fmt - 1, 1);
	    if (rv < 0) {
		return rv;
	    }
	    continue;
	}
	fmt0 = fmt - 1;

	/*
	** Gobble up the % format string. Hopefully we have handled all
	** of the strange cases!
	*/
	flags = 0;
	c = *fmt++;
	if (c == '%') {
	    /* quoting a % with %% */
	    rv = (*ss->stuff)(ss, fmt - 1, 1);
	    if (rv < 0) {
		return rv;
	    }
	    continue;
	}

	if( nas != NULL ){
	    /* the fmt contains the Numbered Arguments feature */
	    i = 0;
	    while( c && c != '$' ){	    /* should imporve error check later */
		i = ( i * 10 ) + ( c - '0' );
		c = *fmt++;
	    }

	    if( nas[i-1].type == TYPE_UNKNOWN ){
		if( l10n_debug )
		    printf( "numbered argument type unknown\n" );
		if( nas && ( nas != nasArray ) )
		    JS_DELETE( nas );
		return -1;
	    }

	    ap = nas[i-1].ap;
	    dolPt = fmt;
	    c = *fmt++;
	}

	/*
	 * Examine optional flags.  Note that we do not implement the
	 * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
	 * somewhat ambiguous and not ideal, which is perhaps why
	 * the various sprintf() implementations are inconsistent
	 * on this feature.
	 */
	while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
	    if (c == '-') flags |= _LEFT;
	    if (c == '+') flags |= _SIGNED;
	    if (c == ' ') flags |= _SPACED;
	    if (c == '0') flags |= _ZEROS;
	    c = *fmt++;
	}
	if (flags & _SIGNED) flags &= ~_SPACED;
	if (flags & _LEFT) flags &= ~_ZEROS;

	/* width */
	if (c == '*') {
	    c = *fmt++;
	    width = va_arg(ap, int);
	} else {
	    width = 0;
	    while ((c >= '0') && (c <= '9')) {
		width = (width * 10) + (c - '0');
		c = *fmt++;
	    }
	}

	/* precision */
	prec = -1;
	if (c == '.') {
	    c = *fmt++;
	    if (c == '*') {
		c = *fmt++;
		prec = va_arg(ap, int);
	    } else {
		prec = 0;
		while ((c >= '0') && (c <= '9')) {
		    prec = (prec * 10) + (c - '0');
		    c = *fmt++;
		}
	    }
	}

	/* size */
	type = TYPE_INTN;
	if (c == 'h') {
	    type = TYPE_INT16;
	    c = *fmt++;
	} else if (c == 'L') {
	    /* XXX not quite sure here */
	    type = TYPE_INT64;
	    c = *fmt++;
	} else if (c == 'l') {
	    type = TYPE_INT32;
	    c = *fmt++;
	    if (c == 'l') {
		type = TYPE_INT64;
		c = *fmt++;
	    }
	}

	/* format */
	hexp = hex;
	switch (c) {
	  case 'd': case 'i':			/* decimal/integer */
	    radix = 10;
	    goto fetch_and_convert;

	  case 'o':				/* octal */
	    radix = 8;
	    type |= 1;
	    goto fetch_and_convert;

	  case 'u':				/* unsigned decimal */
	    radix = 10;
	    type |= 1;
	    goto fetch_and_convert;

	  case 'x':				/* unsigned hex */
	    radix = 16;
	    type |= 1;
	    goto fetch_and_convert;

	  case 'X':				/* unsigned HEX */
	    radix = 16;
	    hexp = HEX;
	    type |= 1;
	    goto fetch_and_convert;

	  fetch_and_convert:
	    switch (type) {
	      case TYPE_INT16:
		u.l = va_arg(ap, int);
		if (u.l < 0) {
		    u.l = -u.l;
		    flags |= _NEG;
		}
		goto do_long;
	      case TYPE_UINT16:
		u.l = va_arg(ap, int) & 0xffff;
		goto do_long;
	      case TYPE_INTN:
		u.l = va_arg(ap, int);
		if (u.l < 0) {
		    u.l = -u.l;
		    flags |= _NEG;
		}
		goto do_long;
	      case TYPE_UINTN:
		u.l = (long)va_arg(ap, unsigned int);
		goto do_long;

	      case TYPE_INT32:
		u.l = va_arg(ap, JSInt32);
		if (u.l < 0) {
		    u.l = -u.l;
		    flags |= _NEG;
		}
		goto do_long;
	      case TYPE_UINT32:
		u.l = (long)va_arg(ap, JSUint32);
	      do_long:
		rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
		if (rv < 0) {
		    return rv;
		}
		break;

	      case TYPE_INT64:
		u.ll = va_arg(ap, JSInt64);
		if (!JSLL_GE_ZERO(u.ll)) {
		    JSLL_NEG(u.ll, u.ll);
		    flags |= _NEG;
		}
		goto do_longlong;
	      case TYPE_UINT64:
		u.ll = va_arg(ap, JSUint64);
	      do_longlong:
		rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
		if (rv < 0) {
		    return rv;
		}
		break;
	    }
	    break;

	  case 'e':
	  case 'E':
	  case 'f':
	  case 'g':
	    u.d = va_arg(ap, double);
	    if( nas != NULL ){
		i = fmt - dolPt;
		if( i < sizeof( pattern ) ){
		    pattern[0] = '%';
		    memcpy( &pattern[1], dolPt, i );
		    rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );
		}
	    } else
		rv = cvt_f(ss, u.d, fmt0, fmt);

	    if (rv < 0) {
		return rv;
	    }
	    break;

	  case 'c':
	    u.ch = va_arg(ap, int);
            if ((flags & _LEFT) == 0) {
                while (width-- > 1) {
                    rv = (*ss->stuff)(ss, " ", 1);
                    if (rv < 0) {
                        return rv;
                    }
                }
            }
	    rv = (*ss->stuff)(ss, &u.ch, 1);
	    if (rv < 0) {
		return rv;
	    }
            if (flags & _LEFT) {
                while (width-- > 1) {
                    rv = (*ss->stuff)(ss, " ", 1);
                    if (rv < 0) {
                        return rv;
                    }
                }
            }
	    break;

	  case 'p':
	    if (sizeof(void *) == sizeof(JSInt32)) {
	    	type = TYPE_UINT32;
	    } else if (sizeof(void *) == sizeof(JSInt64)) {
	    	type = TYPE_UINT64;
	    } else if (sizeof(void *) == sizeof(int)) {
		type = TYPE_UINTN;
	    } else {
		JS_ASSERT(0);
		break;
	    }
	    radix = 16;
	    goto fetch_and_convert;

#if 0
	  case 'C':
	  case 'S':
	  case 'E':
	  case 'G':
	    /* XXX not supported I suppose */
	    JS_ASSERT(0);
	    break;
#endif

	  case 's':
	    u.s = va_arg(ap, const char*);
	    rv = cvt_s(ss, u.s, width, prec, flags);
	    if (rv < 0) {
		return rv;
	    }
	    break;

	  case 'n':
	    u.ip = va_arg(ap, int*);
	    if (u.ip) {
		*u.ip = ss->cur - ss->base;
	    }
	    break;

	  default:
	    /* Not a % token after all... skip it */
#if 0
	    JS_ASSERT(0);
#endif
	    rv = (*ss->stuff)(ss, "%", 1);
	    if (rv < 0) {
		return rv;
	    }
	    rv = (*ss->stuff)(ss, fmt - 1, 1);
	    if (rv < 0) {
		return rv;
	    }
	}
    }

    /* Stuff trailing NUL */
    rv = (*ss->stuff)(ss, "\0", 1);

    if( nas && ( nas != nasArray ) ){
	JS_DELETE( nas );
    }

    return rv;
}

/************************************************************************/

static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)
{
    int rv;

    rv = (*ss->func)(ss->arg, sp, len);
    if (rv < 0) {
	return rv;
    }
    ss->maxlen += len;
    return 0;
}

JS_EXPORT_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg, 
                                 const char *fmt, ...)
{
    va_list ap;
    int rv;

    va_start(ap, fmt);
    rv = JS_vsxprintf(func, arg, fmt, ap);
    va_end(ap);
    return rv;
}

JS_EXPORT_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg, 
                                  const char *fmt, va_list ap)
{
    SprintfState ss;
    int rv;

    ss.stuff = FuncStuff;
    ss.func = func;
    ss.arg = arg;
    ss.maxlen = 0;
    rv = dosprintf(&ss, fmt, ap);
    return (rv < 0) ? (JSUint32)-1 : ss.maxlen;
}

/*
** Stuff routine that automatically grows the malloc'd output buffer
** before it overflows.
*/
static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len)
{
    ptrdiff_t off;
    char *newbase;
    JSUint32 newlen;

    off = ss->cur - ss->base;
    if (off + len >= ss->maxlen) {
	/* Grow the buffer */
	newlen = ss->maxlen + ((len > 32) ? len : 32);
	if (ss->base) {
	    newbase = (char*) realloc(ss->base, newlen);
	} else {
	    newbase = (char*) malloc(newlen);
	}
	if (!newbase) {
	    /* Ran out of memory */
	    return -1;
	}
	ss->base = newbase;
	ss->maxlen = newlen;
	ss->cur = ss->base + off;
    }

    /* Copy data */
    while (len) {
	--len;
	*ss->cur++ = *sp++;
    }
    JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen);
    return 0;
}

/*
** sprintf into a malloc'd buffer
*/
JS_EXPORT_API(char *) JS_smprintf(const char *fmt, ...)
{
    va_list ap;
    char *rv;

    va_start(ap, fmt);
    rv = JS_vsmprintf(fmt, ap);
    va_end(ap);
    return rv;
}

/*
** Free memory allocated, for the caller, by JS_smprintf
*/
JS_EXPORT_API(void) JS_smprintf_free(char *mem)
{
	JS_DELETE(mem);
}

JS_EXPORT_API(char *) JS_vsmprintf(const char *fmt, va_list ap)
{
    SprintfState ss;
    int rv;

    ss.stuff = GrowStuff;
    ss.base = 0;
    ss.cur = 0;
    ss.maxlen = 0;
    rv = dosprintf(&ss, fmt, ap);
    if (rv < 0) {
	if (ss.base) {
	    JS_DELETE(ss.base);
	}
	return 0;
    }
    return ss.base;
}

/*
** Stuff routine that discards overflow data
*/
static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len)
{
    JSUint32 limit = ss->maxlen - (ss->cur - ss->base);

    if (len > limit) {
	len = limit;
    }
    while (len) {
	--len;
	*ss->cur++ = *sp++;
    }
    return 0;
}

/*
** sprintf into a fixed size buffer. Make sure there is a NUL at the end
** when finished.
*/
JS_EXPORT_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...)
{
    va_list ap;
    int rv;

    JS_ASSERT((JSInt32)outlen > 0);
    if ((JSInt32)outlen <= 0) {
	return 0;
    }

    va_start(ap, fmt);
    rv = JS_vsnprintf(out, outlen, fmt, ap);
    va_end(ap);
    return rv;
}

JS_EXPORT_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt,
                                  va_list ap)
{
    SprintfState ss;
    JSUint32 n;

    JS_ASSERT((JSInt32)outlen > 0);
    if ((JSInt32)outlen <= 0) {
	return 0;
    }

    ss.stuff = LimitStuff;
    ss.base = out;
    ss.cur = out;
    ss.maxlen = outlen;
    (void) dosprintf(&ss, fmt, ap);

    /* If we added chars, and we didn't append a null, do it now. */
    if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )
        *(--ss.cur) = '\0';

    n = ss.cur - ss.base;
    return n ? n - 1 : n;
}

JS_EXPORT_API(char *) JS_sprintf_append(char *last, const char *fmt, ...)
{
    va_list ap;
    char *rv;

    va_start(ap, fmt);
    rv = JS_vsprintf_append(last, fmt, ap);
    va_end(ap);
    return rv;
}

JS_EXPORT_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap)
{
    SprintfState ss;
    int rv;

    ss.stuff = GrowStuff;
    if (last) {
	int lastlen = strlen(last);
	ss.base = last;
	ss.cur = last + lastlen;
	ss.maxlen = lastlen;
    } else {
	ss.base = 0;
	ss.cur = 0;
	ss.maxlen = 0;
    }
    rv = dosprintf(&ss, fmt, ap);
    if (rv < 0) {
	if (ss.base) {
	    JS_DELETE(ss.base);
	}
	return 0;
    }
    return ss.base;
}


**** End of jsprf.c. ****

**** Start of jsprf.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsprf_h___
#define jsprf_h___

/*
** API for PR printf like routines. Supports the following formats
**	%d - decimal
**	%u - unsigned decimal
**	%x - unsigned hex
**	%X - unsigned uppercase hex
**	%o - unsigned octal
**	%hd, %hu, %hx, %hX, %ho - 16-bit versions of above
**	%ld, %lu, %lx, %lX, %lo - 32-bit versions of above
**	%lld, %llu, %llx, %llX, %llo - 64 bit versions of above
**	%s - string
**	%c - character
**	%p - pointer (deals with machine dependent pointer size)
**	%f - float
**	%g - float
*/
#include "jstypes.h"
#include <stdio.h>
#include <stdarg.h>

JS_BEGIN_EXTERN_C

/*
** sprintf into a fixed size buffer. Guarantees that a NUL is at the end
** of the buffer. Returns the length of the written output, NOT including
** the NUL, or (JSUint32)-1 if an error occurs.
*/
JS_EXTERN_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...);

/*
** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
** buffer on success, NULL on failure. Call "JS_smprintf_free" to release
** the memory returned.
*/
JS_EXTERN_API(char*) JS_smprintf(const char *fmt, ...);

/*
** Free the memory allocated, for the caller, by JS_smprintf
*/
JS_EXTERN_API(void) JS_smprintf_free(char *mem);

/*
** "append" sprintf into a malloc'd buffer. "last" is the last value of
** the malloc'd buffer. sprintf will append data to the end of last,
** growing it as necessary using realloc. If last is NULL, JS_sprintf_append
** will allocate the initial string. The return value is the new value of
** last for subsequent calls, or NULL if there is a malloc failure.
*/
JS_EXTERN_API(char*) JS_sprintf_append(char *last, const char *fmt, ...);

/*
** sprintf into a function. The function "f" is called with a string to
** place into the output. "arg" is an opaque pointer used by the stuff
** function to hold any state needed to do the storage of the output
** data. The return value is a count of the number of characters fed to
** the stuff function, or (JSUint32)-1 if an error occurs.
*/
typedef JSIntn (*JSStuffFunc)(void *arg, const char *s, JSUint32 slen);

JS_EXTERN_API(JSUint32) JS_sxprintf(JSStuffFunc f, void *arg, const char *fmt, ...);

/*
** va_list forms of the above.
*/
JS_EXTERN_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen, const char *fmt, va_list ap);
JS_EXTERN_API(char*) JS_vsmprintf(const char *fmt, va_list ap);
JS_EXTERN_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap);
JS_EXTERN_API(JSUint32) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap);

/*
***************************************************************************
** FUNCTION: JS_sscanf
** DESCRIPTION:
**     JS_sscanf() scans the input character string, performs data
**     conversions, and stores the converted values in the data objects
**     pointed to by its arguments according to the format control
**     string.
**
**     JS_sscanf() behaves the same way as the sscanf() function in the
**     Standard C Library (stdio.h), with the following exceptions:
**     - JS_sscanf() handles the NSPR integer and floating point types,
**       such as JSInt16, JSInt32, JSInt64, and JSFloat64, whereas
**       sscanf() handles the standard C types like short, int, long,
**       and double.
**     - JS_sscanf() has no multibyte character support, while sscanf()
**       does.
** INPUTS:
**     const char *buf
**         a character string holding the input to scan
**     const char *fmt
**         the format control string for the conversions
**     ...
**         variable number of arguments, each of them is a pointer to
**         a data object in which the converted value will be stored
** OUTPUTS: none
** RETURNS: JSInt32
**     The number of values converted and stored.
** RESTRICTIONS:
**    Multibyte characters in 'buf' or 'fmt' are not allowed.
***************************************************************************
*/

JS_EXTERN_API(JSInt32) JS_sscanf(const char *buf, const char *fmt, ...);

JS_END_EXTERN_C

#endif /* jsprf_h___ */

**** End of jsprf.h. ****

**** Start of jsprhash.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef JSplhash_h___
#define JSplhash_h___
/*
 * API to portable hash table code.
 */
#include <stddef.h>
#include <stdio.h>
#include "jsprtypes.h"

JSPR_BEGIN_EXTERN_C

typedef struct JSPRHashEntry  JSPRHashEntry;
typedef struct JSPRHashTable  JSPRHashTable;
typedef JSPRUint32 JSPRHashNumber;
#define JSPR_HASH_BITS 32
typedef JSPRHashNumber (JSJS_DLL_CALLBACK *JSPRHashFunction)(const void *key);
typedef JSPRIntn (JSJS_DLL_CALLBACK *JSPRHashComparator)(const void *v1, const void *v2);
typedef JSPRIntn (JSJS_DLL_CALLBACK *JSPRHashEnumerator)(JSPRHashEntry *he, JSPRIntn i, void *arg);

/* Flag bits in JSPRHashEnumerator's return value */
#define HT_ENUMERATE_NEXT       0       /* continue enumerating entries */
#define HT_ENUMERATE_STOP       1       /* stop enumerating entries */
#define HT_ENUMERATE_REMOVE     2       /* remove and free the current entry */
#define HT_ENUMERATE_UNHASH     4       /* just unhash the current entry */

typedef struct JSPRHashAllocOps {
    void *              (JSJS_DLL_CALLBACK *allocTable)(void *pool, JSPRSize size);
    void                (JSJS_DLL_CALLBACK *freeTable)(void *pool, void *item);
    JSPRHashEntry *       (JSJS_DLL_CALLBACK *allocEntry)(void *pool, const void *key);
    void                (JSJS_DLL_CALLBACK *freeEntry)(void *pool, JSPRHashEntry *he, JSPRUintn flag);
} JSPRHashAllocOps;

#define HT_FREE_VALUE   0               /* just free the entry's value */
#define HT_FREE_ENTRY   1               /* free value and entire entry */

struct JSPRHashEntry {
    JSPRHashEntry         *next;          /* hash chain linkage */
    JSPRHashNumber        keyHash;        /* key hash function result */
    const void          *key;           /* ptr to opaque key */
    void                *value;         /* ptr to opaque value */
};

struct JSPRHashTable {
    JSPRHashEntry         **buckets;      /* vector of hash buckets */
    JSPRUint32              nentries;       /* number of entries in table */
    JSPRUint32              shift;          /* multiplicative hash shift */
    JSPRHashFunction      keyHash;        /* key hash function */
    JSPRHashComparator    keyCompare;     /* key comparison function */
    JSPRHashComparator    valueCompare;   /* value comparison function */
    JSPRHashAllocOps      *allocOps;      /* allocation operations */
    void                *allocPriv;     /* allocation private data */
#ifdef HASHMETER
    JSPRUint32              nlookups;       /* total number of lookups */
    JSPRUint32              nsteps;         /* number of hash chains traversed */
    JSPRUint32              ngrows;         /* number of table expansions */
    JSPRUint32              nshrinks;       /* number of table contractions */
#endif
};

/*
 * Create a new hash table.
 * If allocOps is null, use default allocator ops built on top of malloc().
 */
JSJS_EXTERN_API(JSPRHashTable *)
JSPR_NewHashTable(JSPRUint32 n, JSPRHashFunction keyHash,
                JSPRHashComparator keyCompare, JSPRHashComparator valueCompare,
                JSPRHashAllocOps *allocOps, void *allocPriv);

JSJS_EXTERN_API(void)
JSPR_HashTableDestroy(JSPRHashTable *ht);

/* Low level access methods */
JSJS_EXTERN_API(JSPRHashEntry **)
JSPR_HashTableRawLookup(JSPRHashTable *ht, JSPRHashNumber keyHash, const void *key);

JSJS_EXTERN_API(JSPRHashEntry *)
JSPR_HashTableRawAdd(JSPRHashTable *ht, JSPRHashEntry **hep, JSPRHashNumber keyHash,
                   const void *key, void *value);

JSJS_EXTERN_API(void)
JSPR_HashTableRawRemove(JSPRHashTable *ht, JSPRHashEntry **hep, JSPRHashEntry *he);

/* Higher level access methods */
JSJS_EXTERN_API(JSPRHashEntry *)
JSPR_HashTableAdd(JSPRHashTable *ht, const void *key, void *value);

JSJS_EXTERN_API(JSPRBool)
JSPR_HashTableRemove(JSPRHashTable *ht, const void *key);

JSJS_EXTERN_API(JSPRIntn)
JSPR_HashTableEnumerateEntries(JSPRHashTable *ht, JSPRHashEnumerator f, void *arg);

JSJS_EXTERN_API(void *)
JSPR_HashTableLookup(JSPRHashTable *ht, const void *key);

JSJS_EXTERN_API(JSPRIntn)
JSPR_HashTableDump(JSPRHashTable *ht, JSPRHashEnumerator dump, FILE *fp);

/* General-purpose C string hash function. */
JSJS_EXTERN_API(JSPRHashNumber)
JSPR_HashString(const void *key);

/* Compare strings using strcmp(), return true if equal. */
JSJS_EXTERN_API(int)
JSPR_CompareStrings(const void *v1, const void *v2);

/* Stub function just returns v1 == v2 */
JSJS_EXTERN_API(JSPRIntn)
JSPR_CompareValues(const void *v1, const void *v2);

JSPR_END_EXTERN_C

#endif /* JSplhash_h___ */

**** End of jsprhash.h. ****

**** Start of jsprvtd.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsprvtd_h___
#define jsprvtd_h___
/*
 * JS private typename definitions.
 *
 * This header is included only in other .h files, for convenience and for
 * simplicity of type naming.  The alternative for structures is to use tags,
 * which are named the same as their typedef names (legal in C/C++, and less
 * noisy than suffixing the typedef name with "Struct" or "Str").  Instead,
 * all .h files that include this file may use the same typedef name, whether
 * declaring a pointer to struct type, or defining a member of struct type.
 *
 * A few fundamental scalar types are defined here too.  Neither the scalar
 * nor the struct typedefs should change much, therefore the nearly-global
 * make dependency induced by this file should not prove painful.
 */

#include "jspubtd.h"

/* Scalar typedefs. */
typedef uint8  jsbytecode;
typedef uint8  jssrcnote;
typedef uint32 jsatomid;

/* Struct typedefs. */
typedef struct JSCodeGenerator  JSCodeGenerator;
typedef struct JSGCThing        JSGCThing;
typedef struct JSParseNode      JSParseNode;
typedef struct JSSharpObjectMap JSSharpObjectMap;
typedef struct JSToken          JSToken;
typedef struct JSTokenPos       JSTokenPos;
typedef struct JSTokenPtr       JSTokenPtr;
typedef struct JSTokenStream    JSTokenStream;
typedef struct JSTreeContext    JSTreeContext;
typedef struct JSTryNote       JSTryNote;

/* Friend "Advanced API" typedefs. */
typedef struct JSAtom           JSAtom;
typedef struct JSAtomList       JSAtomList;
typedef struct JSAtomListElement JSAtomListElement;
typedef struct JSAtomMap        JSAtomMap;
typedef struct JSAtomState      JSAtomState;
typedef struct JSCodeSpec       JSCodeSpec;
typedef struct JSPrinter        JSPrinter;
typedef struct JSRegExp         JSRegExp;
typedef struct JSRegExpStatics  JSRegExpStatics;
typedef struct JSScope          JSScope;
typedef struct JSScopeOps       JSScopeOps;
typedef struct JSScopeProperty  JSScopeProperty;
typedef struct JSStackFrame     JSStackFrame;
typedef struct JSSubString      JSSubString;
typedef struct JSSymbol         JSSymbol;

/* "Friend" types used by jscntxt.h and jsdbgapi.h. */
typedef enum JSTrapStatus {
    JSTRAP_ERROR,
    JSTRAP_CONTINUE,
    JSTRAP_RETURN,
    JSTRAP_THROW,
    JSTRAP_LIMIT
} JSTrapStatus;

#ifndef CRT_CALL
#ifdef XP_OS2
#define CRT_CALL _Optlink
#else
#define CRT_CALL
#endif
#endif

typedef JSTrapStatus
(* CRT_CALL JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, 
                           jsval *rval, void *closure);

typedef JSBool
(* CRT_CALL JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id,
                                 jsval old, jsval *newp, void *closure);

/* called just after script creation */
typedef void
(* CRT_CALL JSNewScriptHook)(JSContext  *cx,
                             const char *filename,  /* URL of script */
                             uintN      lineno,     /* line script starts */
                             JSScript   *script,
                             JSFunction *fun,
                             void       *callerdata);

/* called just before script destruction */
typedef void
(* CRT_CALL JSDestroyScriptHook)(JSContext *cx, 
                                 JSScript  *script, 
                                 void      *callerdata);

typedef void
(* CRT_CALL JSSourceHandler)(const char *filename, uintN lineno, 
                             jschar *str, size_t length, 
                             void **listenerTSData, void *closure);

/*
* This hook captures high level script execution and function calls
* (JS or native). 
* It is used by JS_SetExecuteHook to hook top level scripts and by
* JS_SetCallHook to hook function calls.
* It will get called twice per script or function call:
* just before execution begins and just after it finishes. In both cases
* the 'current' frame is that of the executing code.
*
* The 'before' param is JS_TRUE for the hook invocation before the execution
* and JS_FALSE for the invocation after the code has run.
*
* The 'ok' param is significant only on the post execution invocation to
* signify whether or not the code completed 'normally'.
*
* The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook 
* for the 'before'invocation, but is whatever value is returned from that 
* invocation for the 'after' invocation. Thus, the hook implementor *could*
* allocate a stucture in the 'before' invocation and return a pointer
* to that structure. The pointer would then be handed to the hook for
* the 'after' invocation. Alternately, the 'before' could just return the
* same value as in 'closure' to cause the 'after' invocation to be called 
* with the same 'closure' value as the 'before'.
*
* Returning NULL in the 'before' hook will cause the 'after' hook to
* NOT be called.
*/

typedef void *
(* CRT_CALL JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before,
                               JSBool *ok, void *closure);

typedef void
(* CRT_CALL JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, 
                          void *closure);

typedef JSBool
(* CRT_CALL JSDebugErrorHook)(JSContext *cx, const char *message,
                              JSErrorReport *report, void *closure);

#endif /* jsprvtd_h___ */

**** End of jsprvtd.h. ****

**** Start of jspubtd.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jspubtd_h___
#define jspubtd_h___
/*
 * JS public API typedefs.
 */
#ifndef jstypes_h___
	#include "jstypes.h"
#endif
#ifndef jscompat_h___
	#include "jscompat.h"
#endif

JS_BEGIN_EXTERN_C

/* Scalar typedefs. */
typedef uint16    jschar;
typedef int32     jsint;
typedef uint32    jsuint;
typedef float64   jsdouble;
typedef jsword    jsval;
typedef jsword    jsid;
typedef jsword    jsrefcount;

typedef enum JSVersion {
    JSVERSION_1_0     = 100,
    JSVERSION_1_1     = 110,
    JSVERSION_1_2     = 120,
    JSVERSION_1_3     = 130,
    JSVERSION_1_4     = 140,
    JSVERSION_DEFAULT = 0,
    JSVERSION_UNKNOWN = -1
} JSVersion;

#define JSVERSION_IS_ECMA(version) \
    ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3)

/* Result of typeof operator enumeration. */
typedef enum JSType {
    JSTYPE_VOID,                /* undefined */
    JSTYPE_OBJECT,              /* object */
    JSTYPE_FUNCTION,            /* function */
    JSTYPE_STRING,              /* string */
    JSTYPE_NUMBER,              /* number */
    JSTYPE_BOOLEAN,             /* boolean */
    JSTYPE_LIMIT
} JSType;

/* JSObjectOps.checkAccess mode enumeration. */
typedef enum JSAccessMode {
    JSACC_PROTO,
    JSACC_PARENT,
    JSACC_IMPORT,
    JSACC_WATCH,
    JSACC_LIMIT
} JSAccessMode;

/*
 * This enum type is used to control the behavior of a JSObject property
 * iterator function that has type JSNewEnumerate.
 */
typedef enum JSIterateOp {
    JSENUMERATE_INIT,       /* Create new iterator state */
    JSENUMERATE_NEXT,       /* Iterate once */
    JSENUMERATE_DESTROY     /* Destroy iterator state */
} JSIterateOp;

/* Struct typedefs. */
typedef struct JSClass           JSClass;
typedef struct JSConstDoubleSpec JSConstDoubleSpec;
typedef struct JSContext         JSContext;
typedef struct JSErrorReport     JSErrorReport;
typedef struct JSFunction        JSFunction;
typedef struct JSFunctionSpec    JSFunctionSpec;
typedef struct JSIdArray         JSIdArray;
typedef struct JSProperty        JSProperty;
typedef struct JSPropertySpec    JSPropertySpec;
typedef struct JSObject          JSObject;
typedef struct JSObjectMap       JSObjectMap;
typedef struct JSObjectOps       JSObjectOps;
typedef struct JSRuntime         JSRuntime;
typedef struct JSRuntime         JSTaskState;	/* XXX deprecated name */
typedef struct JSScript          JSScript;
typedef struct JSString          JSString;
typedef struct JSXDRState	 JSXDRState;
typedef struct JSExceptionState  JSExceptionState;

#ifndef CRT_CALL
#ifdef XP_OS2
#define CRT_CALL _Optlink
#else
#define CRT_CALL
#endif
#endif

/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */

typedef JSBool
(* CRT_CALL JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

/*
 *  This function type is used for callbacks that enumerate the properties of a
 *  JSObject.  The behavior depends on the value of enum_op:
 *
 *    JSENUMERATE_INIT - A new, opaque iterator state should be allocated and
 *           stored in *statep.  (You can use PRIVATE_TO_JSVAL() to store
 *           a pointer in *statep).  The number of properties that will be
 *           enumerated should be returned as an integer jsval in *idp, if idp
 *           is non-NULL.
 *    JSENUMERATE_NEXT - A previously allocated opaque iterator state is passed
 *           in via statep.  Return the next jsid in the iteration using *idp.
 *           The opaque iterator state pointed at by statep is destroyed and
 *           *statep is set to JSVAL_NULL if there are no properties left to
 *           enumerate.
 *    JSENUMERATE_DESTROY - Destroy the opaque iterator previous allocated by
 *           a call to this function with enum_op set to JSENUMERATE_INIT.
 *
 *   The return value is always used to indicate success, with a value of JS_FALSE
 *   for failure.
 */
typedef JSBool
(* CRT_CALL JSNewEnumerateOp)(JSContext *cx, JSObject *obj,
			      JSIterateOp enum_op,
			      jsval *statep, jsid *idp);

typedef JSBool
(* CRT_CALL JSEnumerateOp)(JSContext *cx, JSObject *obj);

typedef JSBool
(* CRT_CALL JSResolveOp)(JSContext *cx, JSObject *obj, jsval id);

typedef JSBool
(* CRT_CALL JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, uintN flags,
			    JSObject **objp);

typedef JSBool
(* CRT_CALL JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, jsval *vp);

typedef void
(* CRT_CALL JSFinalizeOp)(JSContext *cx, JSObject *obj);

typedef JSObjectOps *
(* CRT_CALL JSGetObjectOps)(JSContext *cx, JSClass *clasp);

typedef JSBool
(* CRT_CALL JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id,
			     JSAccessMode mode, jsval *vp);

typedef JSBool
(* CRT_CALL JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp);

typedef JSBool
(* CRT_CALL JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v,
			     JSBool *bp);

/* JSObjectOps function pointer typedefs. */

typedef JSObjectMap *
(* CRT_CALL JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs,
			      JSObjectOps *ops, JSClass *clasp,
			      JSObject *obj);

typedef void
(* CRT_CALL JSObjectMapOp)(JSContext *cx, JSObjectMap *map);

typedef JSBool
(* CRT_CALL JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id,
			    JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
			    , const char *file, uintN line
#endif
			    );

typedef JSBool
(* CRT_CALL JSDefinePropOp)(JSContext *cx, JSObject *obj, jsid id, jsval value,
			    JSPropertyOp getter, JSPropertyOp setter,
			    uintN attrs, JSProperty **propp);

typedef JSBool
(* CRT_CALL JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp);

typedef JSBool
(* CRT_CALL JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id,
			    JSProperty *prop, uintN *attrsp);

typedef JSBool
(* CRT_CALL JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id,
			       JSAccessMode mode, jsval *vp, uintN *attrsp);

typedef JSObject *
(* CRT_CALL JSObjectOp)(JSContext *cx, JSObject *obj);

typedef void
(* CRT_CALL JSPropertyRefOp)(JSContext *cx, JSObject *obj, JSProperty *prop);

/* Typedef for native functions called by the JS VM. */

typedef JSBool
(* CRT_CALL JSNative)(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		      jsval *rval);

/* Callbacks and their arguments. */

typedef enum JSGCStatus {
    JSGC_BEGIN,
    JSGC_END
} JSGCStatus;

typedef JSBool
(* CRT_CALL JSGCCallback)(JSContext *cx, JSGCStatus status);

typedef JSBool
(* CRT_CALL JSBranchCallback)(JSContext *cx, JSScript *script);

typedef void
(* CRT_CALL JSErrorReporter)(JSContext *cx, const char *message,
			     JSErrorReport *report);

typedef struct JSErrorFormatString {
    const char *format;
    const uintN argCount;
} JSErrorFormatString;

typedef const JSErrorFormatString *
(* CRT_CALL JSErrorCallback)(void *userRef, const char *locale,
			     const uintN errorNumber);

JS_END_EXTERN_C

#endif /* jspubtd_h___ */

**** End of jspubtd.h. ****

**** Start of jsregexp.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS regular expressions, after Perl.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsregexp.h"
#include "jsstr.h"

#if JS_HAS_REGEXPS

typedef struct RENode RENode;

typedef enum REOp {
    REOP_EMPTY    = 0,  /* match rest of input against rest of r.e. */
    REOP_ALT      = 1,  /* alternative subexpressions in kid and next */
    REOP_BOL      = 2,  /* beginning of input (or line if multiline) */
    REOP_EOL      = 3,  /* end of input (or line if multiline) */
    REOP_WBDRY    = 4,  /* match "" at word boundary */
    REOP_WNONBDRY = 5,  /* match "" at word non-boundary */
    REOP_QUANT    = 6,  /* quantified atom: atom{1,2} */
    REOP_STAR     = 7,  /* zero or more occurrences of kid */
    REOP_PLUS     = 8,  /* one or more occurrences of kid */
    REOP_OPT      = 9,  /* optional subexpression in kid */
    REOP_LPAREN   = 10, /* left paren bytecode: kid is u.num'th sub-regexp */
    REOP_RPAREN   = 11, /* right paren bytecode */
    REOP_DOT      = 12, /* stands for any character */
    REOP_CCLASS   = 13, /* character class: [a-f] */
    REOP_DIGIT    = 14, /* match a digit char: [0-9] */
    REOP_NONDIGIT = 15, /* match a non-digit char: [^0-9] */
    REOP_ALNUM    = 16, /* match an alphanumeric char: [0-9a-z_A-Z] */
    REOP_NONALNUM = 17, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */
    REOP_SPACE    = 18, /* match a whitespace char */
    REOP_NONSPACE = 19, /* match a non-whitespace char */
    REOP_BACKREF  = 20, /* back-reference (e.g., \1) to a parenthetical */
    REOP_FLAT     = 21, /* match a flat string */
    REOP_FLAT1    = 22, /* match a single char */
    REOP_JUMP     = 23, /* for deoptimized closure loops */
    REOP_DOTSTAR  = 24, /* optimize .* to use a single opcode */
    REOP_ANCHOR   = 25, /* like .* but skips left context to unanchored r.e. */
    REOP_EOLONLY  = 26, /* $ not preceded by any pattern */
    REOP_UCFLAT   = 27, /* flat Unicode string; len immediate counts chars */
    REOP_UCFLAT1  = 28, /* single Unicode char */
    REOP_UCCLASS  = 29, /* Unicode character class, vector of chars to match */
    REOP_NUCCLASS = 30, /* negated Unicode character class */
    REOP_BACKREFi = 31, /* case-independent REOP_BACKREF */
    REOP_FLATi    = 32, /* case-independent REOP_FLAT */
    REOP_FLAT1i   = 33, /* case-independent REOP_FLAT1 */
    REOP_UCFLATi  = 34, /* case-independent REOP_UCFLAT */
    REOP_UCFLAT1i = 35, /* case-independent REOP_UCFLAT1 */
    REOP_ANCHOR1  = 36, /* first-char discriminating REOP_ANCHOR */
    REOP_NCCLASS  = 37, /* negated 8-bit character class */
    REOP_END
} REOp;

#define CCLASS_CHARSET_SIZE 256 /* ISO-Latin-1 */

uint8 reopsize[] = {
    /* EMPTY */         1,
    /* ALT */           3,
    /* BOL */           1,
    /* EOL */           1,
    /* WBDRY */         1,
    /* WNONBDRY */      1,
    /* QUANT */         7,
    /* STAR */          1,
    /* PLUS */          1,
    /* OPT */           1,
    /* LPAREN */        3,
    /* RPAREN */        3,
    /* DOT */           1,
    /* CCLASS */        1 + (CCLASS_CHARSET_SIZE / JS_BITS_PER_BYTE),
    /* DIGIT */         1,
    /* NONDIGIT */      1,
    /* ALNUM */         1,
    /* NONALNUM */      1,
    /* SPACE */         1,
    /* NONSPACE */      1,
    /* BACKREF */       2,
    /* FLAT */          2,	/* (2 = op + len) + [len bytes] */
    /* FLAT1 */         2,
    /* JUMP */          3,
    /* DOTSTAR */       1,
    /* ANCHOR */        1,
    /* EOLONLY */       1,
    /* UCFLAT */        2,	/* (2 = op + len) + [len 2-byte chars] */
    /* UCFLAT1 */       3,      /* op + hibyte + lobyte */
    /* UCCLASS */       3,      /* (3 = op + 2-byte len) + [len bytes] */
    /* NUCCLASS */      3,      /* (3 = op + 2-byte len) + [len bytes] */
    /* BACKREFi */      2,
    /* FLATi */         2,	/* (2 = op + len) + [len bytes] */
    /* FLAT1i */        2,
    /* UCFLATi */       2,	/* (2 = op + len) + [len 2-byte chars] */
    /* UCFLAT1i */      3,      /* op + hibyte + lobyte */
    /* ANCHOR1 */       1,
    /* NCCLASS */       1 + (CCLASS_CHARSET_SIZE / JS_BITS_PER_BYTE),
    /* END */           0,
};

#define REOP_FLATLEN_MAX 255    /* maximum length of FLAT string */

struct RENode {
    uint8           op;         /* packed r.e. op bytecode */
    uint8           flags;      /* flags, see below */
    uint16          offset;     /* bytecode offset */
    RENode          *next;      /* next in concatenation order */
    void            *kid;       /* first operand */
    union {
	void        *kid2;      /* second operand */
	jsint       num;        /* could be a number */
	jschar      chr;        /* or a character */
	struct {                /* or a quantifier range */
	    uint16  min;
	    uint16  max;
	} range;
	struct {                /* or a Unicode character class */
	    uint16  kidlen;     /* length of string at kid, in jschars */
	    uint16  bmsize;     /* bitmap size, based on max char code */
	} ucclass;
    } u;
};

#define REOP(ren)   ((REOp)(ren)->op)

#define RENODE_ANCHORED 0x01    /* anchored at the front */
#define RENODE_SINGLE   0x02    /* matches a single char */
#define RENODE_NONEMPTY 0x04    /* does not match empty string */
#define RENODE_ISNEXT   0x08    /* ren is next after at least one node */
#define RENODE_GOODNEXT 0x10    /* ren->next is a tree-like edge in the graph */
#define RENODE_ISJOIN   0x20    /* ren is a join point in the graph */
#define RENODE_REALLOK  0x40    /* REOP_FLAT owns tempPool space to realloc */

typedef struct CompilerState {
    JSContext       *context;
    const jschar    *cpbegin;
    const jschar    *cp;
    uintN           flags;
    uintN           parenCount;
    size_t          progLength;
} CompilerState;

static RENode *
NewRENode(CompilerState *state, REOp op, void *kid)
{
    JSContext *cx;
    RENode *ren;

    cx = state->context;
    JS_ARENA_ALLOCATE(ren, &cx->tempPool, sizeof *ren);
    if (!ren) {
	JS_ReportOutOfMemory(cx);
	return NULL;
    }
    ren->op = (uint8)op;
    ren->flags = 0;
    ren->offset = 0;
    ren->next = NULL;
    ren->kid = kid;
    return ren;
}

#ifdef DEBUG

#include <stdio.h>

static char *reopname[] = {
    "empty",
    "alt",
    "bol",
    "eol",
    "wbdry",
    "wnonbdry",
    "quant",
    "star",
    "plus",
    "opt",
    "lparen",
    "rparen",
    "dot",
    "cclass",
    "digit",
    "nondigit",
    "alnum",
    "nonalnum",
    "space",
    "nonspace",
    "backref",
    "flat",
    "flat1",
    "jump",
    "dotstar",
    "anchor",
    "eolonly",
    "ucflat",
    "ucflat1",
    "ucclass",
    "nucclass",
    "backrefi",
    "flati",
    "flat1i",
    "ucflati",
    "ucflat1i",
    "anchor1",
    "ncclass",
    "end"
};

static void
PrintChar(jschar c)
{
    if (c >> 8)
	printf("\\u%04X", c);
    else
#if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800
	putchar((char)c);
#else
	/* XXX is there a better way with MSVC1.52? */
	printf("%c", c);
#endif
}

static JSBool
DumpRegExp(JSContext *cx, RENode *ren)
{
    static int level;
    JSBool ok;
    int i, len;
    jschar *cp;
    char *cstr;

    if (level == 0)
	printf("level offset  flags  description\n");
    level++;
    ok = JS_TRUE;
    do {
	printf("%5d %6d %c%c%c%c%c%c  %s",
	       level,
	       (int)ren->offset,
	       (ren->flags & RENODE_ANCHORED) ? 'A' : '-',
	       (ren->flags & RENODE_SINGLE)   ? 'S' : '-',
	       (ren->flags & RENODE_NONEMPTY) ? 'F' : '-',	/* F for full */
	       (ren->flags & RENODE_ISNEXT)   ? 'N' : '-',	/* N for next */
	       (ren->flags & RENODE_GOODNEXT) ? 'G' : '-',
	       (ren->flags & RENODE_ISJOIN)   ? 'J' : '-',
	       reopname[ren->op]);

	switch (REOP(ren)) {
	  case REOP_ALT:
	    printf(" %d\n", ren->next->offset);
	    ok = DumpRegExp(cx, ren->kid);
	    if (!ok)
		goto out;
	    break;

	  case REOP_STAR:
	  case REOP_PLUS:
	  case REOP_OPT:
	  case REOP_ANCHOR1:
	    printf("\n");
	    ok = DumpRegExp(cx, ren->kid);
	    if (!ok)
		goto out;
	    break;

	  case REOP_QUANT:
	    printf(" next %d min %d max %d\n",
		   ren->next->offset, ren->u.range.min, ren->u.range.max);
	    ok = DumpRegExp(cx, ren->kid);
	    if (!ok)
		goto out;
	    break;

	  case REOP_LPAREN:
	    printf(" num %d\n", (int)ren->u.num);
	    ok = DumpRegExp(cx, ren->kid);
	    if (!ok)
		goto out;
	    break;

	  case REOP_RPAREN:
	    printf(" num %d\n", (int)ren->u.num);
	    break;

	  case REOP_CCLASS:
	    len = (jschar *)ren->u.kid2 - (jschar *)ren->kid;
	    cstr = js_DeflateString(cx, (jschar *)ren->kid, len);
	    if (!cstr) {
		ok = JS_FALSE;
		goto out;
	    }
	    printf(" [%s]\n", cstr);
	    JS_free(cx, cstr);
	    break;

	  case REOP_BACKREF:
	    printf(" num %d\n", (int)ren->u.num);
	    break;

	  case REOP_FLAT:
	    len = (jschar *)ren->u.kid2 - (jschar *)ren->kid;
	    cstr = js_DeflateString(cx, (jschar *)ren->kid, len);
	    if (!cstr) {
		ok = JS_FALSE;
		goto out;
	    }
	    printf(" %s (%d)\n", cstr, len);
	    JS_free(cx, cstr);
	    break;

	  case REOP_FLAT1:
	    printf(" %c ('\\%o')\n", (char)ren->u.chr, ren->u.chr);
	    break;

	  case REOP_JUMP:
	    printf(" %d\n", ren->next->offset);
	    break;

	  case REOP_UCFLAT:
	    cp = ren->kid;
	    len = (jschar *)ren->u.kid2 - cp;
	    for (i = 0; i < len; i++)
		PrintChar(cp[i]);
	    break;

	  case REOP_UCFLAT1:
	    PrintChar(ren->u.chr);
	    break;

	  case REOP_UCCLASS:
	    cp = ren->kid;
	    len = (jschar *)ren->u.kid2 - cp;
	    printf(" [");
	    for (i = 0; i < len; i++)
		PrintChar(cp[i]);
	    printf("]\n");
	    break;

	  default:
	    printf("\n");
	    break;
	}

	if (!(ren->flags & RENODE_GOODNEXT))
	    break;
    } while ((ren = ren->next) != NULL);
out:
    level--;
    return ok;
}

#endif /* DEBUG */

static JSBool
FixNext(CompilerState *state, RENode *ren1, RENode *ren2, RENode *oldnext)
{
    JSBool goodnext;
    RENode *next, *kid, *ren;

    goodnext = ren2 && !(ren2->flags & RENODE_ISNEXT);

    /*
     * Find the final node in a list of alternatives, or concatenations, or
     * even a concatenation of alternatives followed by non-alternatives (e.g.
     * ((x|y)z)w where ((x|y)z) is ren1 and w is ren2).
     */
    for (; (next = ren1->next) != NULL && next != oldnext; ren1 = next) {
	if (REOP(ren1) == REOP_ALT) {
	    /* Find the end of this alternative's operand list. */
	    kid = ren1->kid;
	    if (REOP(kid) == REOP_JUMP)
		continue;
	    for (ren = kid; ren->next; ren = ren->next)
		JS_ASSERT(REOP(ren) != REOP_ALT);

	    /* Append a jump node to all but the last alternative. */
	    ren->next = NewRENode(state, REOP_JUMP, NULL);
	    if (!ren->next)
		return JS_FALSE;
	    ren->next->flags |= RENODE_ISNEXT;
	    ren->flags |= RENODE_GOODNEXT;

	    /* Recur to fix all descendent nested alternatives. */
	    if (!FixNext(state, kid, ren2, oldnext))
		return JS_FALSE;
	}
    }

    /*
     * Now ren1 points to the last alternative, or to the final node on a
     * concatenation list.  Set its next link to ren2, flagging a join point
     * if appropriate.
     */
    if (ren2) {
	if (!(ren2->flags & RENODE_ISNEXT))
	    ren2->flags |= RENODE_ISNEXT;
	else
	    ren2->flags |= RENODE_ISJOIN;
    }
    ren1->next = ren2;
    if (goodnext)
	ren1->flags |= RENODE_GOODNEXT;

    /*
     * The following ops have a kid subtree through which to recur.  Here is
     * where we fix the next links under the final ALT node's kid.
     */
    switch (REOP(ren1)) {
      case REOP_ALT:
      case REOP_QUANT:
      case REOP_STAR:
      case REOP_PLUS:
      case REOP_OPT:
      case REOP_LPAREN:
	if (!FixNext(state, ren1->kid, ren2, oldnext))
	    return JS_FALSE;
	break;
      default:;
    }
    return JS_TRUE;
}

static JSBool
SetNext(CompilerState *state, RENode *ren1, RENode *ren2)
{
    return FixNext(state, ren1, ren2, NULL);
}

/*
 * Parser forward declarations.
 */
typedef RENode *REParser(CompilerState *state);

static REParser ParseRegExp;
static REParser ParseAltern;
static REParser ParseItem;
static REParser ParseQuantAtom;
static REParser ParseAtom;

/*
 * Top-down regular expression grammar, based closely on Perl4.
 *
 *  regexp:     altern                  A regular expression is one or more
 *              altern '|' regexp       alternatives separated by vertical bar.
 */
static RENode *
ParseRegExp(CompilerState *state)
{
    RENode *ren, *kid, *ren1, *ren2;
    const jschar *cp;

    ren = ParseAltern(state);
    if (!ren)
	return NULL;
    cp = state->cp;
    if (*cp == '|') {
	kid = ren;
	ren = NewRENode(state, REOP_ALT, kid);
	if (!ren)
	    return NULL;
	ren->flags = kid->flags & (RENODE_ANCHORED | RENODE_NONEMPTY);
	ren1 = ren;
	do {
	    /* (balance: */
	    state->cp = ++cp;
	    if (*cp == '|' || *cp == ')') {
		kid = NewRENode(state, REOP_EMPTY, NULL);
	    } else {
		kid = ParseAltern(state);
		cp = state->cp;
	    }
	    if (!kid)
		return NULL;
	    ren2 = NewRENode(state, REOP_ALT, kid);
	    if (!ren2)
		return NULL;
	    ren1->next = ren2;
	    ren1->flags |= RENODE_GOODNEXT;
	    ren2->flags = (kid->flags & (RENODE_ANCHORED | RENODE_NONEMPTY))
			  | RENODE_ISNEXT;
	    ren1 = ren2;
	} while (*cp == '|');
    }
    return ren;
}

/*
 *  altern:     item                    An alternative is one or more items,
 *              item altern             concatenated together.
 */
static RENode *
ParseAltern(CompilerState *state)
{
    RENode *ren, *ren1, *ren2;
    uintN flags;
    const jschar *cp;
    jschar c;

    ren = ren1 = ParseItem(state);
    if (!ren)
	return NULL;
    flags = 0;
    cp = state->cp;
    /* (balance: */
    while ((c = *cp) != 0 && c != '|' && c != ')') {
	ren2 = ParseItem(state);
	if (!ren2)
	    return NULL;
	if (!SetNext(state, ren1, ren2))
	    return NULL;
	flags |= ren2->flags;
	ren1 = ren2;
	cp = state->cp;
    }

    /*
     * Propagate NONEMPTY to the front of a concatenation list, so that the
     * first alternative in (^a|b) is considered non-empty.  The first node
     * in a list may match the empty string (as ^ does), but if the list is
     * non-empty, then the first node's NONEMPTY flag must be set.
     */
    ren->flags |= flags & RENODE_NONEMPTY;
    return ren;
}

/*
 *  item:       assertion               An item is either an assertion or
 *              quantatom               a quantified atom.
 *
 *  assertion:  '^'                     Assertions match beginning of string
 *                                      (or line if the class static property
 *                                      RegExp.multiline is true).
 *              '$'                     End of string (or line if the class
 *                                      static property RegExp.multiline is
 *                                      true).
 *              '\b'                    Word boundary (between \w and \W).
 *              '\B'                    Word non-boundary.
 */
static RENode *
ParseItem(CompilerState *state)
{
    const jschar *cp;
    RENode *ren;
    REOp op;

    cp = state->cp;
    switch (*cp) {
      case '^':
	state->cp = cp + 1;
	ren = NewRENode(state, REOP_BOL, NULL);
	if (ren)
	    ren->flags |= RENODE_ANCHORED;
	return ren;

      case '$':
	state->cp = cp + 1;
	return NewRENode(state,
			 (cp == state->cpbegin ||
			  ((cp[-1] == '(' || cp[-1] == '|') && /*balance)*/
			   (cp - 1 == state->cpbegin || cp[-2] != '\\')))
			 ? REOP_EOLONLY
			 : REOP_EOL,
			 NULL);

      case '\\':
	switch (*++cp) {
	  case 'b':
	    op = REOP_WBDRY;
	    break;
	  case 'B':
	    op = REOP_WNONBDRY;
	    break;
	  default:
	    return ParseQuantAtom(state);
	}

	/*
	 * Word boundaries and non-boundaries are flagged as non-empty so they
	 * will be prefixed by an anchoring node.
	 */
	state->cp = cp + 1;
	ren = NewRENode(state, op, NULL);
	if (ren)
	    ren->flags |= RENODE_NONEMPTY;
	return ren;

      default:;
    }
    return ParseQuantAtom(state);
}

/*
 *  quantatom:  atom                    An unquantified atom.
 *              quantatom '{' n ',' m '}'
 *                                      Atom must occur between n and m times.
 *              quantatom '{' n ',' '}' Atom must occur at least n times.
 *              quantatom '{' n '}'     Atom must occur exactly n times.
 *              quantatom '*'           Zero or more times (same as {0,}).
 *              quantatom '+'           One or more times (same as {1,}).
 *              quantatom '?'           Zero or one time (same as {0,1}).
 */
static RENode *
ParseQuantAtom(CompilerState *state)
{
    RENode *ren, *ren2;
    const jschar *cp, *up;
    jschar c;
    uint32 min, max;

    ren = ParseAtom(state);
    if (!ren)
	return NULL;

    cp = state->cp;
loop:
    switch (*cp) {
      case '{':
	c = *++cp;
	if (!JS7_ISDEC(c)) {
	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				 JSMSG_BAD_QUANTIFIER, state->cp);
	    return NULL;
	}
	min = (uint32)JS7_UNDEC(c);
	for (c = *++cp; JS7_ISDEC(c); c = *++cp) {
	    min = 10 * min + (uint32)JS7_UNDEC(c);
	    if (min >> 16) {
		JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				     JSMSG_MIN_TOO_BIG, state->cp);
		return NULL;
	    }
	}
	if (*cp == ',') {
	    up = ++cp;
	    if (JS7_ISDEC(*cp)) {
		max = (uint32)JS7_UNDEC(*cp);
		for (c = *++cp; JS7_ISDEC(c); c = *++cp) {
		    max = 10 * max + (uint32)JS7_UNDEC(c);
		    if (max >> 16) {
			JS_ReportErrorNumber(state->context,
					     js_GetErrorMessage, NULL,
					     JSMSG_MAX_TOO_BIG, up);
			return NULL;
		    }
		}
		if (max == 0)
		    goto zero_quant;
		if (min > max) {
		    JS_ReportErrorNumber(state->context,
					 js_GetErrorMessage, NULL,
					 JSMSG_OUT_OF_ORDER, up);
		    return NULL;
		}
	    } else {
		/* 0 means no upper bound. */
		max = 0;
	    }
	} else {
	    /* Exactly n times. */
	    if (min == 0) {
      zero_quant:
		JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				     JSMSG_ZERO_QUANTIFIER, state->cp);
		return NULL;
	    }
	    max = min;
	}
	if (*cp != '}') {
	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				 JSMSG_UNTERM_QUANTIFIER, state->cp);
	    return NULL;
	}
	cp++;

	ren2 = NewRENode(state, REOP_QUANT, ren);
	if (!ren2)
	    return NULL;
	if (min > 0 && (ren->flags & RENODE_NONEMPTY))
	    ren2->flags |= RENODE_NONEMPTY;
	ren2->u.range.min = (uint16)min;
	ren2->u.range.max = (uint16)max;
	ren = ren2;
	goto loop;

      case '*':
	if (!(ren->flags & RENODE_NONEMPTY)) {
	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				 JSMSG_EMPTY_BEFORE_STAR);
	    return NULL;
	}
	cp++;
	ren = NewRENode(state, REOP_STAR, ren);
	goto loop;

      case '+':
	if (!(ren->flags & RENODE_NONEMPTY)) {
	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				 JSMSG_EMPTY_BEFORE_PLUS);
	    return NULL;
	}
	cp++;
	ren2 = NewRENode(state, REOP_PLUS, ren);
	if (!ren2)
	    return NULL;
	if (ren->flags & RENODE_NONEMPTY)
	    ren2->flags |= RENODE_NONEMPTY;
	ren = ren2;
	goto loop;

      case '?':
	cp++;
	ren = NewRENode(state, REOP_OPT, ren);
	goto loop;
    }

    state->cp = cp;
    return ren;
}

/*
 *  atom:       '(' regexp ')'          A parenthesized regexp (what matched
 *                                      can be addressed using a backreference,
 *                                      see '\' n below).
 *              '.'                     Matches any char except '\n'.
 *              '[' classlist ']'       A character class.
 *              '[' '^' classlist ']'   A negated character class.
 *              '\f'                    Form Feed.
 *              '\n'                    Newline (Line Feed).
 *              '\r'                    Carriage Return.
 *              '\t'                    Horizontal Tab.
 *              '\v'                    Vertical Tab.
 *              '\d'                    A digit (same as [0-9]).
 *              '\D'                    A non-digit.
 *              '\w'                    A word character, [0-9a-z_A-Z].
 *              '\W'                    A non-word character.
 *              '\s'                    A whitespace character, [ \b\f\n\r\t\v].
 *              '\S'                    A non-whitespace character.
 *              '\' n                   A backreference to the nth (n decimal
 *                                      and positive) parenthesized expression.
 *              '\' octal               An octal escape sequence (octal must be
 *                                      two or three digits long, unless it is
 *                                      0 for the null character).
 *              '\x' hex                A hex escape (hex must be two digits).
 *              '\c' ctrl               A control character, ctrl is a letter.
 *              '\' literalatomchar     Any character except one of the above
 *                                      that follow '\' in an atom.
 *              otheratomchar           Any character not first among the other
 *                                      atom right-hand sides.
 */
static jschar metachars[] = {
    '|', '^', '$', '{', '*', '+', '?', '(', ')', '.', '[', '\\', '}', 0
};

static jschar closurechars[] = {
    '{', '*', '+', '?', 0	/* balance} */
};

static RENode *
ParseAtom(CompilerState *state)
{
    const jschar *cp, *ocp;
    uintN num, tmp, len;
    RENode *ren, *ren2;
    jschar c;

    cp = ocp = state->cp;
    switch (*cp) {
      case 0:
	ren = NewRENode(state, REOP_EMPTY, NULL);
	break;

      case '(':
	num = state->parenCount++;	/* \1 is numbered 0, etc. */
	state->cp = cp + 1;
	ren2 = ParseRegExp(state);
	if (!ren2)
	    return NULL;
	cp = state->cp;
	if (*cp != ')') {
	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				 JSMSG_MISSING_PAREN, ocp);
	    return NULL;
	}
	cp++;
	ren = NewRENode(state, REOP_LPAREN, ren2);
	if (!ren)
	    return NULL;
	ren->flags = ren2->flags & (RENODE_ANCHORED | RENODE_NONEMPTY);
	ren->u.num = num;
	ren2 = NewRENode(state, REOP_RPAREN, NULL);
	if (!ren2 || !SetNext(state, ren, ren2))
	    return NULL;
	ren2->u.num = num;
	break;

      case '.':
	cp++;
	if ((c = *cp) == '*')
	    cp++;
	ren = NewRENode(state, (c == '*') ? REOP_DOTSTAR : REOP_DOT, NULL);
	if (ren && REOP(ren) == REOP_DOT)
	    ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;
	break;

      case '[':
	/* A char class must have at least one char in it. */
	if ((c = *++cp) == 0)
	    goto bad_cclass;

	ren = NewRENode(state, REOP_CCLASS, (void *)cp);
	if (!ren)
	    return NULL;

	/* A negated class must have at least one char in it after the ^. */
	if (c == '^' && *++cp == 0)
	    goto bad_cclass;

	while ((c = *++cp) != ']') {
	    if (c == 0) {
      bad_cclass:
		JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				     JSMSG_UNTERM_CLASS, ocp);
		return NULL;
	    }
	    if (c == '\\' && cp[1] != 0)
		cp++;
	}
	ren->u.kid2 = (void *)cp++;

	/* Since we rule out [] and [^], we can set the non-empty flag. */
	ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;
	break;

      case '\\':
	c = *++cp;
	switch (c) {
	  case 0:
	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,
				 JSMSG_TRAILING_SLASH);
	    return NULL;

	  case 'f':
	  case 'n':
	  case 'r':
	  case 't':
	  case 'v':
	    ren = NewRENode(state, REOP_FLAT1, NULL);
	    c = js_strchr(js_EscapeMap, c)[-1];
	    break;

	  case 'd':
	    ren = NewRENode(state, REOP_DIGIT, NULL);
	    break;
	  case 'D':
	    ren = NewRENode(state, REOP_NONDIGIT, NULL);
	    break;
	  case 'w':
	    ren = NewRENode(state, REOP_ALNUM, NULL);
	    break;
	  case 'W':
	    ren = NewRENode(state, REOP_NONALNUM, NULL);
	    break;
	  case 's':
	    ren = NewRENode(state, REOP_SPACE, NULL);
	    break;
	  case 'S':
	    ren = NewRENode(state, REOP_NONSPACE, NULL);
	    break;

	  case '0':
	  do_octal:
	    num = 0;
	    while ('0' <= (c = *++cp) && c <= '7') {
		tmp = 8 * num + (uintN)JS7_UNDEC(c);
		if (tmp > 0377)
		    break;
		num = tmp;
	    }
	    cp--;
	    ren = NewRENode(state, REOP_FLAT1, NULL);
	    c = (jschar)num;
	    break;

	  case '1':
	  case '2':
	  case '3':
	  case '4':
	  case '5':
	  case '6':
	  case '7':
	  case '8':
	  case '9':
	    num = (uintN)JS7_UNDEC(c);
	    for (c = *++cp; JS7_ISDEC(c); c = *++cp)
		num = 10 * num - (uintN)JS7_UNDEC(c);
	    if (num > 9 && num > state->parenCount) {
		cp = ocp;
		goto do_octal;
	    }
	    cp--;
	    ren = NewRENode(state, REOP_BACKREF, NULL);
	    if (!ren)
		return NULL;
	    ren->u.num = num - 1;	/* \1 is numbered 0, etc. */

	    /* Avoid common chr- and flags-setting code after switch. */
	    ren->flags = RENODE_NONEMPTY;
	    goto bump_cp;

	  case 'x':
	    ocp = cp;
	    c = *++cp;
	    if (JS7_ISHEX(c)) {
		num = JS7_UNHEX(c);
		c = *++cp;
		if (JS7_ISHEX(c)) {
		    num <<= 4;
		    num += JS7_UNHEX(c);
		} else {
		    cp--;	/* back up so cp points to last hex char */
		}
	    } else {
		cp = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */
		num = 'x';
	    }
	    ren = NewRENode(state, REOP_FLAT1, NULL);
	    c = (jschar)num;
	    break;

	  case 'c':
	    c = *++cp;
	    if (!JS7_ISLET(c)) {
		cp -= 2;
		ocp = cp;
		goto do_flat;
	    }
	    c = JS_TOUPPER(c);
	    c = JS_TOCTRL(c);
	    ren = NewRENode(state, REOP_FLAT1, NULL);
	    break;

	  case 'u':
	    if (JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
		JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) {
		c = (((((JS7_UNHEX(cp[1]) << 4) + JS7_UNHEX(cp[2])) << 4)
		      + JS7_UNHEX(cp[3])) << 4) + JS7_UNHEX(cp[4]);
		cp += 4;
		ren = NewRENode(state, REOP_FLAT1, NULL);
		break;
	    }

	    /* Unlike Perl \xZZ, we take \uZZZ to be literal-u then ZZZ. */
	    ocp = cp;
	    goto do_flat;

	  default:
	    ocp = cp;
	    goto do_flat;
	}

	/* Common chr- and flags-setting code for escape opcodes. */
	if (ren) {
	    ren->u.chr = c;
	    ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;
	}

      bump_cp:
	/* Skip to next unparsed char. */
	cp++;
	break;

      default:
      do_flat:
	while ((c = *++cp) != 0 && !js_strchr(metachars, c))
	    ;
	len = (uintN)(cp - ocp);
	if (c != 0 && len > 1 && js_strchr(closurechars, c)) {
	    cp--;
	    len--;
	}
	if (len > REOP_FLATLEN_MAX) {
	    len = REOP_FLATLEN_MAX;
	    cp = ocp + len;
	}
	ren = NewRENode(state, (len == 1) ? REOP_FLAT1 : REOP_FLAT,
			(void *)ocp);
	if (!ren)
	    return NULL;
	ren->flags = RENODE_NONEMPTY;
	if (len > 1) {
	    ren->u.kid2 = (void *)cp;
	} else {
	    ren->flags |= RENODE_SINGLE;
	    ren->u.chr = *ocp;
	}
	break;
    }

    state->cp = cp;
    return ren;
}

static ptrdiff_t
CountFirstChars(RENode *alt)
{
    ptrdiff_t len, sublen;
    RENode *kid;
    jschar c, *ccp, *ccend;

    len = 0;
    do {
	for (kid = alt->kid; REOP(kid) == REOP_LPAREN; kid = kid->kid)
	    ;
	switch (REOP(kid)) {
	  case REOP_QUANT:
	    if (kid->u.range.min == 0)
		return -1;
	    /* FALL THROUGH */
	  case REOP_PLUS:
	  case REOP_ALT:
	    sublen = CountFirstChars(kid);
	    if (sublen < 0)
		return sublen;
	    len += sublen;
	    break;
	  case REOP_FLAT:
	    c = *(jschar *)kid->kid;
	    goto count_char;
	  case REOP_FLAT1:
	    c = kid->u.chr;
	  count_char:
	    /* Only '\\' and '-' need escaping within a character class. */
	    if (c == '\\' || c == '-')
		len += 2;
	    else
		len++;
	    break;
	  case REOP_CCLASS:
	    ccp = kid->kid;
	    if (*ccp == '^')
		return -1;
	    ccend = kid->u.kid2;
	    len += ccend - ccp;
	    break;
	  case REOP_DIGIT:
	  case REOP_NONDIGIT:
	  case REOP_ALNUM:
	  case REOP_NONALNUM:
	  case REOP_SPACE:
	  case REOP_NONSPACE:
	    len += 2;
	    break;
	  default:
	    return -1;
	}
	/* Test for non-alt so quant and plus execute to here only. */
	if (REOP(alt) != REOP_ALT)
	    break;
	alt = alt->next;
    } while (alt && REOP(alt) == REOP_ALT);
    return len;
}

static ptrdiff_t
StoreChar(jschar *cp, ptrdiff_t i, jschar c, JSBool escape)
{
    ptrdiff_t j;

    /* Suppress dups to avoid making a flat1 into a cclass. */
    for (j = 0; j < i; j++) {
	if (cp[j] == '\\')
	    j++;
	if (cp[j] == c && (!escape || (j > 0 && cp[j-1] == '\\')))
	    return i;
    }

    /* Only '\\' and '-' need escaping within a character class. */
    if (escape || c == '\\' || c == '-')
	cp[i++] = '\\';
    cp[i++] = c;
    return i;
}

static ptrdiff_t
StoreFirstChars(RENode *alt, jschar *cp, ptrdiff_t i)
{
    RENode *kid;
    jschar *ccp, *ccend;

    do {
	for (kid = alt->kid; REOP(kid) == REOP_LPAREN; kid = kid->kid)
	    ;
	switch (REOP(kid)) {
	  case REOP_QUANT:
	    JS_ASSERT(kid->u.range.min != 0);
	    /* FALL THROUGH */
	  case REOP_PLUS:
	  case REOP_ALT:
	    i = StoreFirstChars(kid, cp, i);
	    break;
	  case REOP_FLAT:
	    i = StoreChar(cp, i, *(jschar *)kid->kid, JS_FALSE);
	    break;
	  case REOP_FLAT1:
	    i = StoreChar(cp, i, kid->u.chr, JS_FALSE);
	    break;
	  case REOP_CCLASS:
	    ccend = kid->u.kid2;
	    for (ccp = kid->kid; ccp < ccend; ccp++)
		cp[i++] = *ccp;
	    break;
	  case REOP_DIGIT:
	    i = StoreChar(cp, i, 'd', JS_TRUE);
	    break;
	  case REOP_NONDIGIT:
	    i = StoreChar(cp, i, 'D', JS_TRUE);
	    break;
	  case REOP_ALNUM:
	    i = StoreChar(cp, i, 'w', JS_TRUE);
	    break;
	  case REOP_NONALNUM:
	    i = StoreChar(cp, i, 'W', JS_TRUE);
	    break;
	  case REOP_SPACE:
	    i = StoreChar(cp, i, 's', JS_TRUE);
	    break;
	  case REOP_NONSPACE:
	    i = StoreChar(cp, i, 'S', JS_TRUE);
	    break;
	  default:
	    JS_ASSERT(0);
	}
	/* Test for non-alt so quant and plus execute to here only. */
	if (REOP(alt) != REOP_ALT)
	    break;
	alt = alt->next;
    } while (alt && REOP(alt) == REOP_ALT);
    return i;
}

static JSBool
AnchorRegExp(CompilerState *state, RENode *ren)
{
    RENode *ren2, *kid;
    ptrdiff_t len;
    jschar *cp;
    REOp op;

    for (ren2 = ren; REOP(ren2) == REOP_LPAREN; ren2 = ren2->kid)
	;
    len = 0; /* Avoid warning. */
    switch (REOP(ren2)) {
      case REOP_ALT:
	len = CountFirstChars(ren2);
	if (len <= 0)
	    goto do_anchor;
	JS_ARENA_ALLOCATE(cp, &state->context->tempPool, len * sizeof(jschar));
	if (!cp) {
	    JS_ReportOutOfMemory(state->context);
	    return JS_FALSE;
	}

	len = StoreFirstChars(ren2, cp, 0);
	if (len == 1) {
	    op = REOP_FLAT1;
	} else if (len == 2 && *cp == '\\') {
	    switch (cp[1]) {
	      case '\\':
	      case '-':
		/* No need for a character class if just '\\' or '-'. */
		cp++;
		op = REOP_FLAT1;
		break;
	      case 'd':
		op = REOP_DIGIT;
		break;
	      case 'D':
		op = REOP_NONDIGIT;
		break;
	      case 'w':
		op = REOP_ALNUM;
		break;
	      case 'W':
		op = REOP_NONALNUM;
		break;
	      case 's':
		op = REOP_SPACE;
		break;
	      case 'S':
		op = REOP_NONSPACE;
		break;
	      default:
		op = REOP_CCLASS;
		break;
	    }
	} else {
	    op = REOP_CCLASS;
	}

      do_first_char:
	kid = NewRENode(state, op, cp);
	if (!kid)
	    return JS_FALSE;
	kid->flags = RENODE_SINGLE | RENODE_NONEMPTY;
	if (op == REOP_FLAT1)
	    kid->u.chr = *cp;
	else if (op == REOP_CCLASS)
	    kid->u.kid2 = cp + len;

	ren2 = NewRENode(state, REOP(ren), ren->kid);
	if (!ren2)
	    return JS_FALSE;
	ren2->flags = ren->flags | RENODE_ISNEXT;
	ren2->next = ren->next;
	ren2->u = ren->u;

	ren->op = REOP_ANCHOR1;
	ren->flags = RENODE_GOODNEXT;
	ren->next = ren2;
	ren->kid = kid;
	ren->u.kid2 = NULL;
	break;

      case REOP_FLAT:
	cp = ren2->kid;
	op = REOP_FLAT1;
	goto do_first_char;

      case REOP_FLAT1:
	cp = &ren2->u.chr;
	op = REOP_FLAT1;
	goto do_first_char;

      case REOP_DOTSTAR:
	/*
	 * ".*" is anchored by definition when at the front of a list.
	 */
	break;

      default:
      do_anchor:
	/*
	 * Any node other than dotstar that's unanchored and nonempty must be
	 * prefixed by REOP_ANCHOR.
	 */
	JS_ASSERT(REOP(ren2) != REOP_ANCHOR);
	JS_ASSERT(!(ren2->flags & RENODE_ISNEXT));
	if ((ren2->flags & (RENODE_ANCHORED | RENODE_NONEMPTY))
	    == RENODE_NONEMPTY) {
	    ren2 = NewRENode(state, REOP(ren), ren->kid);
	    if (!ren2)
		return JS_FALSE;
	    ren2->flags = ren->flags | RENODE_ISNEXT;
	    ren2->next = ren->next;
	    ren2->u = ren->u;
	    ren->op = REOP_ANCHOR;
	    ren->flags = RENODE_GOODNEXT;
	    ren->next = ren2;
	    ren->kid = ren->u.kid2 = NULL;
	}
	break;
    }
    return JS_TRUE;
}

static RENode *
CloseTail(CompilerState *state, RENode *alt1, RENode *next)
{
    RENode *alt2, *empty;

    empty = NewRENode(state, REOP_EMPTY, NULL);
    alt2 = NewRENode(state, REOP_ALT, empty);
    if (!alt2 || !empty)
	return NULL;
    alt1->next = alt2;
    alt2->next = empty->next = next;
    if (alt1->flags & RENODE_GOODNEXT)
	alt2->flags |= RENODE_GOODNEXT;
    else
	alt1->flags |= RENODE_GOODNEXT;
    alt2->flags |= RENODE_ISNEXT;
    return alt2;
}

static JSBool
OptimizeRegExp(CompilerState *state, RENode *ren)
{
    RENode *kid, *next, *jump, *alt1;
    uintN flag;
    jschar c, c2, maxc, *cp, *cp2;
    ptrdiff_t len, len2;
    size_t size, incr;
    JSContext *cx;
    JSBool reallok;

    do {
	switch (REOP(ren)) {
	  case REOP_STAR:
	    kid = ren->kid;
	    if (!(kid->flags & RENODE_SINGLE)) {
		/*
		 * If kid is not simple, deoptimize <kid>* as follows (the |__|
		 * are byte placeholders for next/jump offsets):
		 *
		 * FROM: |STAR|<kid>|
		 *
		 *       +-----------------------+
		 *       V                       |
		 * TO:   |ALT|__|__|<kid>|JUMP|__|__|ALT|__|__|EMPTY|
		 *              |                   ^      |        ^
		 *              +-------------------+      +--------+
		 */
		ren->op = REOP_ALT;
		next = ren->next;
		jump = NewRENode(state, REOP_JUMP, NULL);
		if (!jump || !FixNext(state, kid, jump, next))
		    return JS_FALSE;
		jump->next = ren;
		if (ren->flags & RENODE_ISNEXT)
		    ren->flags |= RENODE_ISJOIN;
		if (!CloseTail(state, ren, next))
		    return JS_FALSE;
	    }
	    break;

	  case REOP_PLUS:
	    kid = ren->kid;
	    if (!(kid->flags & RENODE_SINGLE)) {
		/*
		 * FROM: |PLUS|<kid>|
		 *
		 *       +-----------------------+
		 *       V                       |
		 * TO:   |<kid>|ALT|__|__|JUMP|__|__|ALT|__|__|EMPTY|
		 *                    |             ^      |        ^
		 *                    +-------------+      +--------+
		 */
		next = ren->next;
		flag = (ren->flags & RENODE_GOODNEXT);
		*ren = *kid;
		jump = NewRENode(state, REOP_JUMP, NULL);
		alt1 = NewRENode(state, REOP_ALT, jump);
		if (!alt1 || !jump || !FixNext(state, ren, alt1, next))
		    return JS_FALSE;
		alt1->flags |= flag;
		jump->next = ren;
		if (ren->flags & RENODE_ISNEXT)
		    ren->flags |= RENODE_ISJOIN;
		if (!CloseTail(state, alt1, next))
		    return JS_FALSE;
	    }
	    break;

	  case REOP_OPT:
	    kid = ren->kid;
	    if (!(kid->flags & RENODE_SINGLE)) {
		/*
		 * FROM: |OPT|<kid>|
		 *
		 *                               +------------------+
		 *                               |                  v
		 * TO:   |ALT|__|__|<kid>|JUMP|__|__|ALT|__|__|EMPTY|
		 *              |                   ^      |        ^
		 *              +-------------------+      +--------+
		 */
		ren->op = REOP_ALT;
		next = ren->next;
		jump = NewRENode(state, REOP_JUMP, NULL);
		if (!jump || !FixNext(state, kid, jump, next))
		    return JS_FALSE;
		jump->next = next;
		if (!CloseTail(state, ren, next))
		    return JS_FALSE;
		next->flags |= RENODE_ISJOIN;
	    }
	    break;

	  case REOP_FLAT:
	    /*
	     * Coalesce adjacent FLAT and FLAT1 nodes.  Also coalesce FLAT and
	     * FLAT, which can result from deleting a coalesced FLAT1.
	     */
	    while ((next = ren->next) != NULL &&
		   !(next->flags & RENODE_ISJOIN) &&
		   (REOP(next) == REOP_FLAT || REOP(next) == REOP_FLAT1)) {
		if (REOP(next) == REOP_FLAT) {
		    cp2 = next->kid;
		    len2 = PTRDIFF((jschar *)next->u.kid2, cp2, jschar);
		} else {
		    cp2 = &next->u.chr;
		    len2 = 1;
		}
		cp = ren->kid;
		len = PTRDIFF((jschar *)ren->u.kid2, cp, jschar);
		if (len + len2 > REOP_FLATLEN_MAX)
		    break;
		cx = state->context;
		reallok = ren->flags & RENODE_REALLOK;
		if (reallok) {
		    /* Try to extend the last alloc, to fuse FLAT,FLAT1,... */
		    size = (len + 1) * sizeof(jschar);
		    incr = len2 * sizeof(jschar);
		    JS_ARENA_GROW(cp, &cx->tempPool, size, incr);
		} else {
		    size = (len + len2 + 1) * sizeof(jschar);
		    JS_ARENA_ALLOCATE(cp, &cx->tempPool, size);
		}
		if (!cp) {
		    JS_ReportOutOfMemory(cx);
		    return JS_FALSE;
		}
		if (!reallok) {
		    js_strncpy(cp, ren->kid, len);
		    ren->flags |= RENODE_REALLOK;
		}
		js_strncpy(&cp[len], cp2, len2);
		len += len2;
		cp[len] = 0;
	  end_coalesce:
		ren->kid = cp;
		JS_ASSERT(ren->flags & RENODE_GOODNEXT);
		if (!(next->flags & RENODE_GOODNEXT))
		    ren->flags &= ~RENODE_GOODNEXT;
		ren->u.kid2 = cp + len;
		ren->next = next->next;
		next->op = REOP_EMPTY;	/* next should be unreachable! */
	    }
	    break;

	  case REOP_FLAT1:
	    /*
	     * Coalesce adjacent FLAT1 nodes.  Also coalesce FLAT1 and FLAT.
	     * After a single coalesce, we reuse the REOP_FLAT case's code by
	     * jumping into the bottom of its while loop.
	     */
	    next = ren->next;
	    if (next &&
		!(next->flags & RENODE_ISJOIN) &&
		(REOP(next) == REOP_FLAT || REOP(next) == REOP_FLAT1)) {
		if (REOP(next) == REOP_FLAT) {
		    cp2 = next->kid;
		    len = PTRDIFF((jschar *)next->u.kid2, cp2, jschar);
		} else {
		    cp2 = &next->u.chr;
		    len = 1;
		}
		cx = state->context;
		JS_ARENA_ALLOCATE(cp, &cx->tempPool, (len+2) * sizeof(jschar));
		if (!cp) {
		    JS_ReportOutOfMemory(cx);
		    return JS_FALSE;
		}
		cp[0] = ren->u.chr;
		js_strncpy(&cp[1], cp2, len);
		cp[++len] = 0;
		ren->op = REOP_FLAT;
		ren->flags |= RENODE_REALLOK;
		goto end_coalesce;
	    }
	    break;

	  default:;
	}

	/*
	 * Set ren's offset and advance progLength by ren's base size.
	 */
	ren->offset = (uint16) state->progLength;
	state->progLength += reopsize[ren->op];

	switch (REOP(ren)) {
	  case REOP_ALT:
	  case REOP_QUANT:
	  case REOP_STAR:
	  case REOP_PLUS:
	  case REOP_OPT:
	  case REOP_LPAREN:
	  case REOP_ANCHOR1:
	    /*
	     * Recur for nodes that have kid links to other nodes.
	     */
	    if (!OptimizeRegExp(state, ren->kid))
		return JS_FALSE;
	    break;

	  case REOP_CCLASS:
	    /*
	     * Check for a nonzero high byte or a \uXXXX escape sequence.
	     */
	    cp  = ren->kid;
	    cp2 = ren->u.kid2;
	    len = PTRDIFF(cp2, cp, jschar);
	    maxc = 0;
	    while (cp < cp2) {
		c = *cp++;
		if (c == '\\') {
		    if (cp + 5 <= cp2 && *cp == 'u' &&
			JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&
			JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) {
			c = (((((JS7_UNHEX(cp[1]) << 4)
				+ JS7_UNHEX(cp[2])) << 4)
			      + JS7_UNHEX(cp[3])) << 4)
			    + JS7_UNHEX(cp[4]);
			cp += 5;
		    } else {
			/*
			 * Octal and hex escapes can't be > 255.  Skip this
			 * backslash and let the loop pass over the remaining
			 * escape sequence as if it were text to match.
			 */
			continue;
		    }
		}
		if (state->flags & JSREG_FOLD) {
		    /*
		     * Don't assume that lowercase are above uppercase, or
		     * that c is either even when c has upper and lowercase
		     * versions.
		     */
		    if ((c2 = JS_TOUPPER(c)) > maxc)
			maxc = c2;
		    if ((c2 = JS_TOLOWER(c2)) > maxc)
			maxc = c2;
		}
		if (c > maxc)
		    maxc = c;
	    }
	    if (maxc >= CCLASS_CHARSET_SIZE) {
		ren->op = (uint8)REOP_UCCLASS;
		size = (size_t)(maxc + JS_BITS_PER_BYTE) / JS_BITS_PER_BYTE;
		ren->u.ucclass.kidlen = (uint16)len;
		ren->u.ucclass.bmsize = (uint16)size;
		state->progLength -= reopsize[REOP_CCLASS];
		state->progLength += reopsize[REOP_UCCLASS] + size;
	    }
	    break;

	  case REOP_FLAT:
	    /*
	     * FLAT takes 2 bytes plus the bytes in the string to match.
	     * If any character has a non-zero high byte, switch to UCFLAT
	     * and double the immediate operand length.
	     */
	    cp  = ren->kid;
	    cp2 = ren->u.kid2;
	    len = PTRDIFF(cp2, cp, jschar);
	    while (cp < cp2) {
		c = *cp++;
		if (c >> 8) {
		    ren->op = (uint8)REOP_UCFLAT;
		    len *= 2;
		    break;
		}
	    }
	    state->progLength += len;
	    break;

	  case REOP_FLAT1:
	    c = ren->u.chr;
	    if (c >> 8) {
		ren->op = (uint8)REOP_UCFLAT1;
		state->progLength++;
	    }
	    break;

	  case REOP_JUMP:
	    /*
	     * Eliminate jumps to jumps.
	     */
	    while ((next = ren->next) != NULL && REOP(next) == REOP_JUMP)
		ren->next = next->next;
	    break;

	  case REOP_END:
	    /*
	     * End of program.
	     */
	    return JS_TRUE;

	  default:;
	}

	if (!(ren->flags & RENODE_GOODNEXT))
	    break;
    } while ((ren = ren->next) != NULL);

    return JS_TRUE;
}

static JSBool
EmitRegExp(CompilerState *state, RENode *ren, JSRegExp *re)
{
    REOp op;
    jsbytecode *pc, fill;
    RENode *next;
    ptrdiff_t diff;
    jschar *cp, *end, *ocp;
    uintN b, c, i, j, n, lastc, foldc, nchars;
    JSBool inrange;

    do {
	op = REOP(ren);
	if (op == REOP_END)
	    return JS_TRUE;

	pc = &re->program[state->progLength];
	state->progLength += reopsize[ren->op];
	pc[0] = ren->op;
	next = ren->next;

	switch (op) {
	  case REOP_ALT:
	    diff = next->offset - ren->offset;
	    SET_JUMP_OFFSET(pc, diff);
	    if (!EmitRegExp(state, ren->kid, re))
		return JS_FALSE;
	    break;

	  case REOP_QUANT:
	    diff = next->offset - ren->offset;
	    SET_JUMP_OFFSET(pc, diff);
	    pc += 2;
	    SET_ARGNO(pc, ren->u.range.min);
	    pc += 2;
	    SET_ARGNO(pc, ren->u.range.max);
	    if (!EmitRegExp(state, ren->kid, re))
		return JS_FALSE;
	    break;

	  case REOP_STAR:
	  case REOP_PLUS:
	  case REOP_OPT:
	  case REOP_ANCHOR1:
	    if (!EmitRegExp(state, ren->kid, re))
		return JS_FALSE;
	    break;

	  case REOP_LPAREN:
	    SET_ARGNO(pc, ren->u.num);
	    if (!EmitRegExp(state, ren->kid, re))
		return JS_FALSE;
	    break;

	  case REOP_RPAREN:
	    SET_ARGNO(pc, ren->u.num);
	    break;

	  case REOP_CCLASS:
	  case REOP_UCCLASS:
	    cp = ren->kid;
	    if (*cp == '^') {
		pc[0] = (jsbytecode)
			((op == REOP_CCLASS) ? REOP_NCCLASS : REOP_NUCCLASS);
		fill = 0xff;
		cp++;
	    } else {
		fill = 0;
	    }
	    pc++;
	    if (op == REOP_CCLASS) {
		end = ren->u.kid2;
		for (i = 0; i < CCLASS_CHARSET_SIZE / JS_BITS_PER_BYTE; i++)
		    pc[i] = fill;
		nchars = CCLASS_CHARSET_SIZE;
	    } else {
		end = cp + ren->u.ucclass.kidlen;
		n = (uintN)ren->u.ucclass.bmsize;
		*pc++ = (jsbytecode)(n >> 8);
		*pc++ = (jsbytecode)n;
		state->progLength += n;
		for (i = 0; i < n; i++)
		    pc[i] = fill;
		nchars = n * JS_BITS_PER_BYTE;
	    }

/* Split ops up into statements to keep MSVC1.52 from crashing. */
#define MATCH_BIT(c)    { i = (c) >> 3; b = (c) & 7; b = 1 << b;              \
			  if (fill) pc[i] &= ~b; else pc[i] |= b; }

	    lastc = nchars;
	    inrange = JS_FALSE;

	    while (cp < end) {
		c = (uintN) *cp++;
		if (c == '\\') {
		    c = *cp++;
		    switch (c) {
		      case 'b':
		      case 'f':
		      case 'n':
		      case 'r':
		      case 't':
		      case 'v':
			c = js_strchr(js_EscapeMap, (jschar)c)[-1];
			break;

#define CHECK_RANGE() if (inrange) { MATCH_BIT(lastc); MATCH_BIT('-');        \
				     inrange = JS_FALSE; }

		      case 'd':
			CHECK_RANGE();
			for (c = '0'; c <= '9'; c++)
			    MATCH_BIT(c);
			continue;

		      case 'D':
			CHECK_RANGE();
			for (c = 0; c < '0'; c++)
			    MATCH_BIT(c);
			for (c = '9' + 1; c < nchars; c++)
			    MATCH_BIT(c);
			continue;

		      case 'w':
			CHECK_RANGE();
			for (c = 0; c < nchars; c++)
			    if (JS_ISWORD(c))
				MATCH_BIT(c);
			continue;

		      case 'W':
			CHECK_RANGE();
			for (c = 0; c < nchars; c++)
			    if (!JS_ISWORD(c))
				MATCH_BIT(c);
			continue;

		      case 's':
			CHECK_RANGE();
			for (c = 0; c < nchars; c++)
			    if (JS_ISSPACE(c))
				MATCH_BIT(c);
			continue;

		      case 'S':
			CHECK_RANGE();
			for (c = 0; c < nchars; c++)
			    if (!JS_ISSPACE(c))
				MATCH_BIT(c);
			continue;

#undef CHECK_RANGE

		      case '0':
		      case '1':
		      case '2':
		      case '3':
		      case '4':
		      case '5':
		      case '6':
		      case '7':
			n = JS7_UNDEC(c);
			ocp = cp - 2;
			c = *cp;
			if ('0' <= c && c <= '7') {
			    cp++;
			    n = 8 * n + JS7_UNDEC(c);

			    c = *cp;
			    if ('0' <= c && c <= '7') {
				cp++;
				i = 8 * n + JS7_UNDEC(c);
				if (i <= 0377)
				    n = i;
				else
				    cp--;
			    }
			}
			c = n;
			break;

		      case 'x':
			ocp = cp;
			c = *cp++;
			if (JS7_ISHEX(c)) {
			    n = JS7_UNHEX(c);
			    c = *cp++;
			    if (JS7_ISHEX(c)) {
				n <<= 4;
				n += JS7_UNHEX(c);
			    }
			} else {
			    cp = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */
			    n = 'x';
			}
			c = n;
			break;

		      case 'u':
			if (JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
			    JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
			    n = (((((JS7_UNHEX(cp[0]) << 4)
				    + JS7_UNHEX(cp[1])) << 4)
				  + JS7_UNHEX(cp[2])) << 4)
				+ JS7_UNHEX(cp[3]);
			    c = n;
			    cp += 4;
			}
			break;

		      case 'c':
			c = *cp++;
			c = JS_TOUPPER(c);
			c = JS_TOCTRL(c);
			break;
		    }
		}

		if (inrange) {
		    if (lastc > c) {
			JS_ReportErrorNumber(state->context,
					     js_GetErrorMessage, NULL,
					     JSMSG_BAD_CLASS_RANGE);
			return JS_FALSE;
		    }
		    inrange = JS_FALSE;
		} else {
		    /* Set lastc so we match just c's bit in the for loop. */
		    lastc = c;

		    /* [balance: */
		    if (*cp == '-' && cp + 1 < end && cp[1] != ']') {
			cp++;
			inrange = JS_TRUE;
			continue;
		    }
		}

		/* Match characters in the range [lastc, c]. */
		for (; lastc <= c; lastc++) {
		    MATCH_BIT(lastc);
		    if (state->flags & JSREG_FOLD) {
			/*
			 * Must do both upper and lower for Turkish dotless i,
			 * Georgian, etc.
			 */
			foldc = JS_TOUPPER(lastc);
			MATCH_BIT(foldc);
			foldc = JS_TOLOWER(foldc);
			MATCH_BIT(foldc);
		    }
		}
		lastc = c;
	    }

#undef MATCH_BIT
	    break;

	  case REOP_BACKREF:
	    if (state->flags & JSREG_FOLD)
		pc[0] = (jsbytecode)REOP_BACKREFi;
	    pc[1] = (jsbytecode)ren->u.num;
	    break;

	  case REOP_FLAT:
	    if (state->flags & JSREG_FOLD)
		pc[0] = (jsbytecode)REOP_FLATi;
	    goto emit_flat;

	  case REOP_UCFLAT:
	    if (state->flags & JSREG_FOLD)
		pc[0] = (jsbytecode)REOP_UCFLATi;
	  emit_flat:
	    cp = ren->kid;
	    diff = (jschar *)ren->u.kid2 - cp;
	    pc[1] = (jsbytecode)diff;
	    pc += 2;
	    state->progLength += diff;
	    if (op == REOP_UCFLAT)
		state->progLength += diff;
	    for (i = j = 0; i < (uintN)diff; i++, j++) {
		c = (uintN)cp[i];

		/*
		 * Lay down immediate chars in native byte order so memcmp
		 * with a JSString's chars works.
		 */
#if IS_BIG_ENDIAN
		if (op == REOP_UCFLAT)
		    pc[j++] = (jsbytecode)(c >> 8);
#endif
		pc[j] = (jsbytecode)c;
#if IS_LITTLE_ENDIAN
		if (op == REOP_UCFLAT)
		    pc[++j] = (jsbytecode)(c >> 8);
#endif
	    }
	    break;

	  case REOP_FLAT1:
	    if (state->flags & JSREG_FOLD)
		pc[0] = (jsbytecode)REOP_FLAT1i;
	    pc[1] = (jsbytecode)ren->u.chr;
	    break;

	  case REOP_UCFLAT1:
	    if (state->flags & JSREG_FOLD)
		pc[0] = (jsbytecode)REOP_UCFLAT1i;
	    c = (uintN)ren->u.chr;
	    pc[1] = (jsbytecode)(c >> 8);
	    pc[2] = (jsbytecode)c;
	    break;

	  case REOP_JUMP:
	    diff = next->offset - ren->offset;
	    SET_JUMP_OFFSET(pc, diff);
	    break;

	  default:;
	}

	if (!(ren->flags & RENODE_GOODNEXT))
	    break;
    } while ((ren = next) != NULL);
    return JS_TRUE;
}

JSRegExp *
js_NewRegExp(JSContext *cx, JSString *str, uintN flags)
{
    JSRegExp *re;
    void *mark;
    CompilerState state;
    RENode *ren, *end;
    size_t resize;

    re = NULL;
    mark = JS_ARENA_MARK(&cx->tempPool);

    state.context = cx;
    state.cpbegin = state.cp = str->chars;
    state.flags = flags;
    state.parenCount = 0;
    state.progLength = 0;

    ren = ParseRegExp(&state);
    if (!ren)
	goto out;

    end = NewRENode(&state, REOP_END, NULL);
    if (!end || !SetNext(&state, ren, end))
	goto out;

    if (!AnchorRegExp(&state, ren))
	goto out;
    if (!OptimizeRegExp(&state, ren))
	goto out;

#ifdef DEBUG_notme
    if (!DumpRegExp(cx, ren))
	goto out;
#endif

    resize = sizeof *re + state.progLength - 1;
    re = JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword)));
    if (!re)
	goto out;
    re->source = str;
    re->length = state.progLength;
    re->lastIndex = 0;
    re->parenCount = state.parenCount;
    re->flags = flags;

    state.progLength = 0;
    if (!EmitRegExp(&state, ren, re)) {
	js_DestroyRegExp(cx, re);
	re = NULL;
	goto out;
    }

#ifdef DEBUG_notme
    {
	/* print the compiled regexp program bytecode */
	size_t i;
	for (i = 0; i < state.progLength; i++) {
	    int b = (int) re->program[i];
	    fprintf(stderr, "%d", b);
	    if ((i > 0 && i % 8 == 0) || i == state.progLength-1)
		fprintf(stderr, "\n");
	    else
		fprintf(stderr, ", ");
	}
	fprintf(stderr, "\n");
    }
#endif

    /* Success: lock re->source string. */
    (void) js_LockGCThing(cx, str);
out:
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return re;
}

JSRegExp *
js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt)
{
    uintN flags;
    jschar *cp;

    flags = 0;
    if (opt) {
	for (cp = opt->chars; *cp; cp++) {
	    switch (*cp) {
	      case 'g':
		flags |= JSREG_GLOB;
		break;
	      case 'i':
		flags |= JSREG_FOLD;
		break;
	      default: {
		char charBuf[2] = " ";
		charBuf[0] = (char)*cp;
		JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				     JSMSG_BAD_FLAG, charBuf);
		return NULL;
	      }
	    }
	}
    }
    return js_NewRegExp(cx, str, flags);
}

void
js_DestroyRegExp(JSContext *cx, JSRegExp *re)
{
    js_UnlockGCThing(cx, re->source);
    JS_free(cx, re);
}

typedef struct MatchState {
    JSContext       *context;           /* for access to regExpStatics */
    JSBool          anchoring;          /* true if multiline anchoring ^/$ */
    jsbytecode      *pcend;             /* pc limit (fencepost) */
    const jschar    *cpbegin, *cpend;   /* cp base address and limit */
    size_t          start;              /* offset from cpbegin to start at */
    ptrdiff_t       skipped;            /* chars skipped anchoring this r.e. */
    uintN           parenCount;         /* number of paren substring matches */
    JSSubString     *maybeParens;       /* possible paren substring pointers */
    JSSubString     *parens;            /* certain paren substring matches */
} MatchState;

/*
 * Returns updated cp on match, null on mismatch.
 */
static const jschar *
MatchRegExp(MatchState *state, jsbytecode *pc, const jschar *cp)
{
    jsbytecode *pc2, *pcend;
    const jschar *cp2, *cp3, *cpbegin, *cpend;
    REOp op;
    JSBool matched;
    ptrdiff_t i, oplen, altlen, matchlen;
    uintN min, max, num;
    JSSubString *parsub;
    const jschar *parstr;
    size_t parlen;
    jschar c, c2;
    uintN bit, byte, size;

    pcend = state->pcend;
    cpbegin = state->cpbegin;
    cpend = state->cpend;

    while (pc < pcend) {
	op = (REOp) *pc;
	oplen = reopsize[op];
        
        matched = JS_FALSE; /* Avoid warnings. */
        matchlen = 0;

	switch (op) {
	  case REOP_EMPTY:
	    pc += oplen;
	    continue;

	  case REOP_ALT:
	    altlen = GET_JUMP_OFFSET(pc);
	    pc2 = pc + oplen;
	    if ((REOp)pc[altlen] != REOP_ALT) {
		pc = pc2;
		continue;
	    }
	    cp2 = MatchRegExp(state, pc2, cp);
	    if (cp2)
		return cp2;
	    pc += altlen;
	    continue;

	  case REOP_BOL:
	    matched = (cp == cpbegin);
	    if (state->context->regExpStatics.multiline) {
		/* Anchor-search only if RegExp.multiline is true. */
		if (state->anchoring) {
		    if (!matched)
			matched = (cp[-1] == '\n');
		} else {
		    state->anchoring = JS_TRUE;
		    for (cp2 = cp; cp2 < cpend; cp2++) {
			if (cp2 == cpbegin || cp2[-1] == '\n') {
			    cp3 = MatchRegExp(state, pc, cp2);
			    if (cp3) {
				state->skipped = cp2 - cp;
				state->anchoring = JS_FALSE;
				return cp3;
			    }
			}
		    }
		    state->anchoring = JS_FALSE;
		}
	    }
	    matchlen = 0;
	    break;

	  case REOP_EOL:
	  case REOP_EOLONLY:
	    matched = (cp == cpend);
	    if (op == REOP_EOL || state->anchoring) {
		if (!matched && state->context->regExpStatics.multiline)
		    matched = (*cp == '\n');
	    } else {
		/* Always anchor-search EOLONLY, which has no BOL analogue. */
		state->anchoring = JS_TRUE;
		for (cp2 = cp; cp2 <= cpend; cp2++) {
		    if (cp2 == cpend || *cp2 == '\n') {
			cp3 = MatchRegExp(state, pc, cp2);
			if (cp3) {
			    state->anchoring = JS_FALSE;
			    state->skipped = cp2 - cp;
			    return cp3;
			}
		    }
		}
		state->anchoring = JS_FALSE;
	    }
	    matchlen = 0;
	    break;

	  case REOP_WBDRY:
	    matched = (cp == cpbegin || !JS_ISWORD(cp[-1])) ^ !JS_ISWORD(*cp);
	    matchlen = 0;
	    break;

	  case REOP_WNONBDRY:
	    matched = (cp == cpbegin || !JS_ISWORD(cp[-1])) ^ JS_ISWORD(*cp);
	    matchlen = 0;
	    break;

	  case REOP_QUANT:
	    pc2 = pc;
	    oplen = GET_JUMP_OFFSET(pc2);
	    pc2 += 2;
	    min = GET_ARGNO(pc2);
	    pc2 += 2;
	    max = GET_ARGNO(pc2);
	    pc2 += 3;

	    /* Reduce state->pcend so we match only the quantified regexp. */
	    state->pcend = pc + oplen;

	    /* If min is non-zero, insist on at least that many matches. */
	    for (num = 0; num < min; num++) {
		cp = MatchRegExp(state, pc2, cp);
		if (!cp) {
		    state->pcend = pcend;
		    return NULL;
		}
	    }

	    /* Try matches from min to max, or forever if max == 0. */
	    for (; !max || num < max; num++) {
		cp2 = MatchRegExp(state, pc2, cp);
		if (!cp2)
		    break;
		cp = cp2;
	    }

	    /* Restore state->pcend and set match and matchlen. */
	    state->pcend = pcend;
	    matched = (min <= num && (!max || num <= max));
	    matchlen = 0;
	    break;

	  case REOP_LPAREN:
	    num = GET_ARGNO(pc);
	    parsub = &state->maybeParens[num];
	    parstr = parsub->chars;
	    parsub->chars = cp;
	    pc += oplen;
	    cp3 = MatchRegExp(state, pc, cp);
	    if (!cp3) {
		/* Restore so later backrefs work, unlike Perl4. */
		parsub->chars = parstr;
		return NULL;
	    }
	    parsub = &state->parens[num];
	    if (!parsub->chars) {
		cp2 = cpbegin + state->start + state->skipped;
		if (cp < cp2) {
		    parsub->chars = cp2;
		    parsub->length -= cp2 - cp;
		} else {
		    parsub->chars = cp;
		}
	    }
	    return cp3;

	  case REOP_RPAREN:
	    num = GET_ARGNO(pc);
	    parsub = &state->maybeParens[num];
	    parsub->length = parlen = cp - parsub->chars;
	    pc += oplen;
	    cp = MatchRegExp(state, pc, cp);
	    if (cp) {
		parsub = &state->parens[num];
		if (!parsub->chars)
		    parsub->length = parlen;
		if (num >= state->parenCount)
		    state->parenCount = num + 1;
	    }
	    return cp;

	  case REOP_BACKREF:
	    num = (uintN)pc[1];
	    parsub = &state->maybeParens[num];
	    matchlen = (ptrdiff_t)parsub->length;
	    matched = (cp + matchlen <= cpend &&
		       !memcmp(cp, parsub->chars, matchlen * sizeof(jschar)));
	    break;

/*
 * See java.lang.String for more on why both toupper and tolower are needed, in
 * comments for equalsIgnoreCase and regionMatches(boolean ignoreCase, ...).
 */
#define MATCH_CHARS_IGNORING_CASE(c, c2)                                      \
    ((c) == (c2) ||                                                           \
     (c = JS_TOUPPER(c)) == (c2 = JS_TOUPPER(c2)) ||                          \
     JS_TOLOWER(c) == JS_TOLOWER(c2))

	  case REOP_BACKREFi:
	    num = (uintN)pc[1];
	    parsub = &state->maybeParens[num];
	    matchlen = (ptrdiff_t)parsub->length;
	    matched = (cp + matchlen <= cpend);
	    if (matched) {
		for (i = 0; i < matchlen; i++) {
		    c  = cp[i];
		    c2 = parsub->chars[i];
		    matched = MATCH_CHARS_IGNORING_CASE(c, c2);
		    if (!matched)
			break;
		}
	    }
	    break;

#define SINGLE_CASES                                                          \
	  case REOP_DOT:                                                      \
	    matched = (cp != cpend && *cp != '\n');                           \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  NONDOT_SINGLE_CASES                                                 \
/* END SINGLE_CASES */

#define NONDOT_SINGLE_CASES                                                   \
	  case REOP_CCLASS:                                                   \
	  case REOP_NCCLASS:                                                  \
	    c = *cp;                                                          \
	    if (c >= CCLASS_CHARSET_SIZE) {                                   \
		matched = (op == REOP_NCCLASS);                               \
	    } else {                                                          \
		byte = (uintN)c >> 3;                                         \
		bit = c & 7;                                                  \
		bit = 1 << bit;                                               \
		matched = pc[1 + byte] & bit;                                 \
	    }                                                                 \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_DIGIT:                                                    \
	    matched = JS_ISDIGIT(*cp);                                        \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_NONDIGIT:                                                 \
	    matched = !JS_ISDIGIT(*cp);                                       \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_ALNUM:                                                    \
	    matched = JS_ISWORD(*cp);                                         \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_NONALNUM:                                                 \
	    matched = !JS_ISWORD(*cp);                                        \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_SPACE:                                                    \
	    matched = JS_ISSPACE(*cp);                                        \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_NONSPACE:                                                 \
	    matched = !JS_ISSPACE(*cp);                                       \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_FLAT1:                                                    \
	    c  = *cp;                                                         \
	    c2 = (jschar)pc[1];                                               \
	    matched = (c == c2);                                              \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_FLAT1i:                                                   \
	    c  = *cp;                                                         \
	    c2 = (jschar)pc[1];                                               \
	    matched = MATCH_CHARS_IGNORING_CASE(c, c2);                       \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_UCFLAT1:                                                  \
	    c  = *cp;                                                         \
	    c2 = ((pc[1] << 8) | pc[2]);                                      \
	    matched = (c == c2);                                              \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_UCFLAT1i:                                                 \
	    c  = *cp;                                                         \
	    c2 = ((pc[1] << 8) | pc[2]);                                      \
	    matched = MATCH_CHARS_IGNORING_CASE(c, c2);                       \
	    matchlen = 1;                                                     \
	    break;                                                            \
									      \
	  case REOP_UCCLASS:                                                  \
	  case REOP_NUCCLASS:                                                 \
	    size = (pc[1] << 8) | pc[2];                                      \
	    oplen += size;                                                    \
	    c = *cp;                                                          \
	    byte = (uintN)c >> 3;                                             \
	    if (byte >= size) {                                               \
		matched = (op == REOP_NUCCLASS);                              \
	    } else {                                                          \
		bit = c & 7;                                                  \
		bit = 1 << bit;                                               \
		matched = pc[3 + byte] & bit;                                 \
	    }                                                                 \
            if (matched) /*SRJ doublebyte fix per DW team*/                               \
                pc2 = pc + size + reopsize[op]; /*SRJ doublebyte fix per DW team*/        \
	    matchlen = 1;                                                     \
	    break;                                                            \
/* END NONDOT_SINGLE_CASES */

	  /*
	   * Macro-expand single-char/single-opcode cases here and below.
	   */
	  SINGLE_CASES

	  case REOP_STAR:
	    op = (REOp) *++pc;
	    oplen = reopsize[op];
	    for (cp2 = cp; cp < cpend; cp++) {
		switch (op) {
		  NONDOT_SINGLE_CASES
		  default:
		    JS_ASSERT(0);
		}
		if (!matched)
		    break;
	    }

	  backtracker:
	    pc += oplen;
	    do {
		cp3 = MatchRegExp(state, pc, cp);
		if (cp3)
		    return cp3;
	    } while (--cp >= cp2);
	    return NULL;

	  case REOP_PLUS:
	    op = (REOp) *++pc;
	    oplen = reopsize[op];
	    for (cp2 = cp; cp < cpend; cp++) {
		switch (op) {
		  SINGLE_CASES
		  default:
		    JS_ASSERT(0);
		}
		if (!matched)
		    break;
	    }
	    if (cp == cp2) {
		/* Did not match once, hope for an alternative. */
		return NULL;
	    }
	    /* Matched one or more times, try rest of regexp. */
	    cp2++;
	    goto backtracker;

	  case REOP_OPT:
	    op = (REOp) *++pc;
	    oplen = reopsize[op];
	    switch (op) {
	      SINGLE_CASES
	      default:
		JS_ASSERT(0);
	    }
	    pc += oplen;
	    if (matched) {
		cp2 = MatchRegExp(state, pc, cp + 1);
		if (cp2)
		    return cp2;
	    }
	    continue;

	  case REOP_FLAT:
	    matchlen = (ptrdiff_t)pc[1];
	    oplen += matchlen;
	    matched = (cp + matchlen <= cpend);
	    if (matched) {
		pc2 = pc + 2;
		for (i = 0; i < matchlen; i++) {
		    matched = (cp[i] == (jschar)pc2[i]);
		    if (!matched)
			break;
		}
	    }
	    break;

	  case REOP_FLATi:
	    matchlen = (ptrdiff_t)pc[1];
	    oplen += matchlen;
	    matched = (cp + matchlen <= cpend);
	    if (matched) {
		pc2 = pc + 2;
		for (i = 0; i < matchlen; i++) {
		    c  = cp[i];
		    c2 = (jschar)pc2[i];
		    matched = MATCH_CHARS_IGNORING_CASE(c, c2);
		    if (!matched)
			break;
		}
	    }
	    break;

	  case REOP_UCFLAT:
	    matchlen = (ptrdiff_t)pc[1];
	    oplen += 2 * matchlen;
	    matched = (cp + matchlen <= cpend &&
		       !memcmp(cp, pc + 2, matchlen * sizeof(jschar)));
	    break;

	  case REOP_UCFLATi:
	    matchlen = (ptrdiff_t)pc[1];
	    oplen += 2 * matchlen;
	    matched = (cp + matchlen <= cpend);
	    if (matched) {
		pc2 = pc + 2;
		for (i = 0; i < matchlen; i++) {
		    c  = cp[i];
#if IS_BIG_ENDIAN
		    c2 = *pc2++ << 8;
		    c2 |= *pc2++;
#endif
#if IS_LITTLE_ENDIAN
		    c2 = *pc2++;
		    c2 |= *pc2++ << 8;
#endif
		    matched = MATCH_CHARS_IGNORING_CASE(c, c2);
		    if (!matched)
			break;
		}
	    }
	    break;

	  case REOP_JUMP:
	    oplen = GET_JUMP_OFFSET(pc);
	    pc += oplen;
	    continue;

	  case REOP_DOTSTAR:
	    for (cp2 = cp; cp2 < cpend; cp2++)
		if (*cp2 == '\n')
		    break;
	    for (pc2 = pc + oplen; cp2 >= cp; cp2--) {
		cp3 = MatchRegExp(state, pc2, cp2);
		if (cp3)
		    return cp3;
	    }
	    return NULL;

	  case REOP_ANCHOR:
	    pc2 = pc + oplen;
	    if (pc2 == pcend)
		break;
	    for (cp2 = cp; cp2 < cpend; cp2++) {
		cp3 = MatchRegExp(state, pc2, cp2);
		if (cp3) {
		    state->skipped = cp2 - cp;
		    return cp3;
		}
	    }
	    return NULL;

	  case REOP_ANCHOR1:
	    op = (REOp) *++pc;
	    oplen = reopsize[op];
	    pc2 = pc + oplen;
	    JS_ASSERT(pc2 < pcend);
	    for (cp2 = cp; cp < cpend; cp++) {
		switch (op) {
		  NONDOT_SINGLE_CASES
		  default:
		    JS_ASSERT(0);
		}
		if (matched) {
		    cp3 = MatchRegExp(state, pc2, cp);
		    if (cp3) {
			state->skipped = cp - cp2;
			return cp3;
		    }
		}
	    }
	    return NULL;

#undef MATCH_CHARS_IGNORING_CASE
#undef SINGLE_CASES
#undef NONDOT_SINGLE_CASES

	  default:
	    JS_ASSERT(0);
	    return NULL;
	}

	if (!matched)
	    return NULL;
	pc += oplen;
	if (matchlen) {
	    cp += matchlen;
	    if (cp > cpend)
		cp = cpend;
	}
    }

    return cp;
}

JSBool
js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
		 JSBool test, jsval *rval)
{
    MatchState state;
    jsbytecode *pc;
    const jschar *cp, *ep;
    size_t i, length, start;
    void *mark;
    JSSubString *parsub, *morepar;
    JSBool ok;
    JSRegExpStatics *res;
    ptrdiff_t matchlen;
    uintN num, morenum;
    JSString *parstr, *matchstr;
    JSObject *obj;

    /*
     * Initialize a state struct to minimize recursive argument traffic.
     */
    state.context = cx;
    state.anchoring = JS_FALSE;
    pc = re->program;
    state.pcend = pc + re->length;

    /*
     * It's safe to load from cp because JSStrings have a zero at the end,
     * and we never let cp get beyond cpend.
     */
    start = *indexp;
    if (start > str->length)
	start = str->length;
    cp = str->chars + start;
    state.cpbegin = str->chars;
    state.cpend = str->chars + str->length;
    state.start = start;
    state.skipped = 0;

    /*
     * Use the temporary arena pool to grab space for parenthetical matches.
     * After the JS_ARENA_ALLOCATE early return on error, goto out to be sure
     * to free this memory.
     */
    length = 2 * sizeof(JSSubString) * re->parenCount;
    mark = JS_ARENA_MARK(&cx->tempPool);
    JS_ARENA_ALLOCATE(parsub, &cx->tempPool, length);
    if (!parsub) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
    memset(parsub, 0, length);
    state.parenCount = 0;
    state.maybeParens = parsub;
    state.parens = parsub + re->parenCount;
    ok = JS_TRUE;

    /*
     * Call the recursive matcher to do the real work.  Return null on mismatch
     * whether testing or not.  On match, return an extended Array object.
     */
    cp = MatchRegExp(&state, pc, cp);
    if (!cp) {
	*rval = JSVAL_NULL;
	goto out;
    }
    i = PTRDIFF(cp, state.cpbegin, jschar);
    *indexp = i;
    matchlen = i - (start + state.skipped);
    ep = cp;
    cp -= matchlen;

    if (test) {
	/*
	 * Testing for a match and updating cx->regExpStatics: don't allocate
	 * an array object, do return true.
	 */
	*rval = JSVAL_TRUE;

        /* Avoid warning.  (gcc doesn't detect that obj is needed iff !test); */
        obj = NULL; 
    } else {
	/*
	 * The array returned on match has element 0 bound to the matched
	 * string, elements 1 through state.parenCount bound to the paren
	 * matches, an index property telling the length of the left context,
	 * and an input property referring to the input string.
	 */
	obj = js_NewArrayObject(cx, 0, NULL);
	if (!obj) {
	    ok = JS_FALSE;
	    goto out;
	}
	*rval = OBJECT_TO_JSVAL(obj);

#define DEFVAL(val, id) {                                                     \
    ok = js_DefineProperty(cx, obj, id, val,                                  \
			   JS_PropertyStub, JS_PropertyStub,                  \
			   JSPROP_ENUMERATE, NULL);                           \
    if (!ok) {                                                                \
	cx->newborn[GCX_OBJECT] = NULL;                                       \
	cx->newborn[GCX_STRING] = NULL;                                       \
	goto out;                                                             \
    }                                                                         \
}

	matchstr = js_NewStringCopyN(cx, cp, matchlen, 0);
	if (!matchstr) {
	    cx->newborn[GCX_OBJECT] = NULL;
	    ok = JS_FALSE;
	    goto out;
	}
	DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSVAL(0));
    }

    res = &cx->regExpStatics;
    JS_ASSERT(state.parenCount <= re->parenCount);
    if (state.parenCount == 0) {
	res->parenCount = 0;
	res->lastParen = js_EmptySubString;
    } else {
	for (num = 0; num < state.parenCount; num++) {
	    parsub = &state.parens[num];
	    if (num < 9) {
		res->parens[num] = *parsub;
	    } else {
		morenum = num - 9;
		morepar = res->moreParens;
		if (!morepar) {
		    res->moreLength = 10;
		    morepar = JS_malloc(cx, 10 * sizeof(JSSubString));
		} else if (morenum > res->moreLength) {
		    res->moreLength += 10;
		    morepar = JS_realloc(cx, morepar,
					 res->moreLength * sizeof(JSSubString));
		}
		if (!morepar) {
		    cx->newborn[GCX_OBJECT] = NULL;
		    cx->newborn[GCX_STRING] = NULL;
		    ok = JS_FALSE;
		    goto out;
		}
		res->moreParens = morepar;
		morepar[morenum] = *parsub;
	    }
	    if (test)
		continue;
	    parstr = js_NewStringCopyN(cx, parsub->chars, parsub->length, 0);
	    if (!parstr) {
		cx->newborn[GCX_OBJECT] = NULL;
		cx->newborn[GCX_STRING] = NULL;
		ok = JS_FALSE;
		goto out;
	    }
	    ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1),
				   STRING_TO_JSVAL(parstr), NULL, NULL,
				   JSPROP_ENUMERATE, NULL);
	    if (!ok) {
		cx->newborn[GCX_OBJECT] = NULL;
		cx->newborn[GCX_STRING] = NULL;
		goto out;
	    }
	}
	res->parenCount = num;
	res->lastParen = *parsub;
    }

    if (!test) {
	/*
	 * Define the index and input properties last for better for/in loop
	 * order (so they come after the elements).
	 */
	DEFVAL(INT_TO_JSVAL(start + state.skipped),
	       (jsid)cx->runtime->atomState.indexAtom);
	DEFVAL(STRING_TO_JSVAL(str),
	       (jsid)cx->runtime->atomState.inputAtom);
    }

#undef DEFVAL

    res->lastMatch.chars = cp;
    res->lastMatch.length = matchlen;
    if (cx->version == JSVERSION_1_2) {
	/*
	 * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used
	 * in scalar contexts, and unintentionally for the string.match "list"
	 * psuedo-context.  On "hi there bye", the following would result:
	 *
	 * Language     while(/ /g){print("$`");}   s/ /$`/g
	 * perl4.036    "hi", "there"               "hihitherehi therebye"
	 * perl5        "hi", "hi there"            "hihitherehi therebye"
	 * js1.2        "hi", "there"               "hihitheretherebye"
	 */
	res->leftContext.chars = str->chars + start;
	res->leftContext.length = state.skipped;
    } else {
	/*
	 * For JS1.3 and ECMAv2, emulate Perl5 exactly:
	 *
	 * js1.3        "hi", "hi there"            "hihitherehi therebye"
	 */
	res->leftContext.chars = str->chars;
	res->leftContext.length = start + state.skipped;
    }
    res->rightContext.chars = ep;
    res->rightContext.length = state.cpend - ep;

out:
    JS_ARENA_RELEASE(&cx->tempPool, mark);
    return ok;
}

/************************************************************************/

enum regexp_tinyid {
    REGEXP_SOURCE       = -1,
    REGEXP_GLOBAL       = -2,
    REGEXP_IGNORE_CASE  = -3,
    REGEXP_LAST_INDEX   = -4
};

static JSPropertySpec regexp_props[] = {
    {"source",      REGEXP_SOURCE,      JSPROP_ENUMERATE | JSPROP_READONLY},
    {"global",      REGEXP_GLOBAL,      JSPROP_ENUMERATE | JSPROP_READONLY},
    {"ignoreCase",  REGEXP_IGNORE_CASE, JSPROP_ENUMERATE | JSPROP_READONLY},
    {"lastIndex",   REGEXP_LAST_INDEX,  JSPROP_ENUMERATE},
    {0}
};

static JSBool
regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint slot;
    JSRegExp *re;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);
    JS_LOCK_OBJ(cx, obj);
    re = JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
    if (re) {
	switch (slot) {
	  case REGEXP_SOURCE:
	    *vp = STRING_TO_JSVAL(re->source);
	    break;
	  case REGEXP_GLOBAL:
	    *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0);
	    break;
	  case REGEXP_IGNORE_CASE:
	    *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0);
	    break;
	  case REGEXP_LAST_INDEX:
	    *vp = INT_TO_JSVAL((jsint)re->lastIndex);
	    break;
	}
    }
    JS_UNLOCK_OBJ(cx, obj);
    return JS_TRUE;
}

static JSBool
regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint slot;
    JSRegExp *re;
    jsdouble d;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);
    JS_LOCK_OBJ(cx, obj);
    re = JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
    if (re && slot == REGEXP_LAST_INDEX) {
	if (!js_ValueToNumber(cx, *vp, &d))
	    return JS_FALSE;
	re->lastIndex = (size_t)d;
    }
    JS_UNLOCK_OBJ(cx, obj);
    return JS_TRUE;
}

/*
 * RegExp class static properties and their Perl counterparts:
 *
 *  RegExp.input                $_
 *  RegExp.multiline            $*
 *  RegExp.lastMatch            $&
 *  RegExp.lastParen            $+
 *  RegExp.leftContext          $`
 *  RegExp.rightContext         $'
 */
enum regexp_static_tinyid {
    REGEXP_STATIC_INPUT         = -1,
    REGEXP_STATIC_MULTILINE     = -2,
    REGEXP_STATIC_LAST_MATCH    = -3,
    REGEXP_STATIC_LAST_PAREN    = -4,
    REGEXP_STATIC_LEFT_CONTEXT  = -5,
    REGEXP_STATIC_RIGHT_CONTEXT = -6
};

JSBool
js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res)
{
    JS_ClearRegExpStatics(cx);
    return js_AddRoot(cx, &res->input, "res->input");
}

void
js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res)
{
    if (res->moreParens) {
	JS_free(cx, res->moreParens);
	res->moreParens = NULL;
    }
    js_RemoveRoot(cx, &res->input);
}

static JSBool
regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint slot;
    JSRegExpStatics *res;
    JSString *str;
    JSSubString *sub;

    res = &cx->regExpStatics;
    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);
    switch (slot) {
      case REGEXP_STATIC_INPUT:
	*vp = res->input ? STRING_TO_JSVAL(res->input)
			 : JS_GetEmptyStringValue(cx);
	return JS_TRUE;
      case REGEXP_STATIC_MULTILINE:
	*vp = BOOLEAN_TO_JSVAL(res->multiline);
	return JS_TRUE;
      case REGEXP_STATIC_LAST_MATCH:
	sub = &res->lastMatch;
	break;
      case REGEXP_STATIC_LAST_PAREN:
	sub = &res->lastParen;
	break;
      case REGEXP_STATIC_LEFT_CONTEXT:
	sub = &res->leftContext;
	break;
      case REGEXP_STATIC_RIGHT_CONTEXT:
	sub = &res->rightContext;
	break;
      default:
	sub = REGEXP_PAREN_SUBSTRING(res, slot);
	break;
    }
    str = js_NewStringCopyN(cx, sub->chars, sub->length, 0);
    if (!str)
	return JS_FALSE;
    *vp = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSRegExpStatics *res;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    res = &cx->regExpStatics;
    /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */
    if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) {
	if (!JSVAL_IS_STRING(*vp) &&
	    !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
	    return JS_FALSE;
	}
	res->input = JSVAL_TO_STRING(*vp);
    } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) {
	if (!JSVAL_IS_BOOLEAN(*vp) &&
	    !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {
	    return JS_FALSE;
	}
	res->multiline = JSVAL_TO_BOOLEAN(*vp);
    }
    return JS_TRUE;
}

static JSPropertySpec regexp_static_props[] = {
    {"input",
     REGEXP_STATIC_INPUT,          JSPROP_ENUMERATE,
     regexp_static_getProperty,    regexp_static_setProperty},
    {"multiline",
     REGEXP_STATIC_MULTILINE,      JSPROP_ENUMERATE,
     regexp_static_getProperty,    regexp_static_setProperty},
    {"lastMatch",
     REGEXP_STATIC_LAST_MATCH,     JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"lastParen",
     REGEXP_STATIC_LAST_PAREN,     JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"leftContext",
     REGEXP_STATIC_LEFT_CONTEXT,   JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"rightContext",
     REGEXP_STATIC_RIGHT_CONTEXT,  JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},

    /* XXX should have block scope and local $1, etc. */
    {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},
    {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY,
     regexp_static_getProperty,    regexp_static_getProperty},

    {0}
};

static void
regexp_finalize(JSContext *cx, JSObject *obj)
{
    JSRegExp *re;

    re = JS_GetPrivate(cx, obj);
    if (!re)
	return;
    js_DestroyRegExp(cx, re);
}

/* Forward static prototype. */
static JSBool
regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	    jsval *rval);

static JSBool
regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
}

#if JS_HAS_XDR

#include "jsxdrapi.h"

static JSBool
regexp_xdrObject(JSXDRState *xdr, JSObject **objp)
{
    JSRegExp *re;
    JSString *source;
    uint8 flags;

    if (xdr->mode == JSXDR_ENCODE) {
	re = JS_GetPrivate(xdr->cx, *objp);
	if (!re)
	    return JS_FALSE;
	source = re->source;
	flags = re->flags;
    }
    if (!JS_XDRString(xdr, &source) ||
	!JS_XDRUint8(xdr, &flags)) {
	return JS_FALSE;
    }
    if (xdr->mode == JSXDR_DECODE) {
	*objp = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL);
	if (!*objp)
	    return JS_FALSE;
	re = js_NewRegExp(xdr->cx, source, flags);
	if (!re)
	    return JS_FALSE;
	if (!JS_SetPrivate(xdr->cx, *objp, re)) {
	    js_DestroyRegExp(xdr->cx, re);
	    return JS_FALSE;
	}
    }
    return JS_TRUE;
}

#else  /* !JS_HAS_XDR */

#define regexp_xdrObject NULL

#endif /* !JS_HAS_XDR */

JSClass js_RegExpClass = {
    "RegExp",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  regexp_getProperty, regexp_setProperty,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,     regexp_finalize,
    NULL,             NULL,             regexp_call,        NULL,
    regexp_xdrObject,
};

static JSBool
regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    JSBool ok;
    JSRegExp *re;
    jschar *chars;
    size_t length, nflags;
    uintN flags;
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
	return JS_FALSE;
    ok = JS_TRUE;
    JS_LOCK_OBJ(cx, obj);
    re = JS_GetPrivate(cx, obj);
    if (!re) {
	*rval = STRING_TO_JSVAL(cx->runtime->emptyString);
	goto out;
    }

    length = re->source->length + 2;
    nflags = 0;
    for (flags = re->flags; flags != 0; flags &= flags - 1)
	nflags++;
    chars = JS_malloc(cx, (length + nflags + 1) * sizeof(jschar));
    if (!chars) {
	ok = JS_FALSE;
	goto out;
    }

    chars[0] = '/';
    js_strncpy(&chars[1], re->source->chars, length - 2);
    chars[length-1] = '/';
    if (nflags) {
	if (re->flags & JSREG_GLOB)
	    chars[length++] = 'g';
	if (re->flags & JSREG_FOLD)
	    chars[length++] = 'i';
    }
    chars[length] = 0;

    str = js_NewString(cx, chars, length, 0);
    if (!str) {
	JS_free(cx, chars);
	ok = JS_FALSE;
	goto out;
    }
    *rval = STRING_TO_JSVAL(str);
out:
    JS_UNLOCK_OBJ(cx, obj);
    return ok;
}

static JSBool
regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	       jsval *rval)
{
    JSString *opt, *str;
    JSRegExp *oldre, *re;
    JSBool ok;

    if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
	return JS_FALSE;
    opt = NULL;
    JS_LOCK_OBJ(cx, obj);
    if (argc == 0) {
	str = cx->runtime->emptyString;
    } else {
	str = js_ValueToString(cx, argv[0]);
	if (!str) {
	    ok = JS_FALSE;
	    goto out;
	}
	argv[0] = STRING_TO_JSVAL(str);
	if (argc > 1) {
	    opt = js_ValueToString(cx, argv[1]);
	    if (!opt) {
		ok = JS_FALSE;
		goto out;
	    }
	    argv[1] = STRING_TO_JSVAL(opt);
	}
    }
    re = js_NewRegExpOpt(cx, str, opt);
    if (!re) {
	ok = JS_FALSE;
	goto out;
    }
    oldre = JS_GetPrivate(cx, obj);
    ok = JS_SetPrivate(cx, obj, re);
    if (!ok) {
	js_DestroyRegExp(cx, re);
	goto out;
    }
    if (oldre)
	js_DestroyRegExp(cx, oldre);
    *rval = OBJECT_TO_JSVAL(obj);
out:
    JS_UNLOCK_OBJ(cx, obj);
    return ok;
}

static JSBool
regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		JSBool test, jsval *rval)
{
    JSBool ok, locked;
    JSRegExp *re;
    JSString *str;
    size_t i;

    if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
	return JS_FALSE;
    re = JS_GetPrivate(cx, obj);
    if (!re)
	return JS_TRUE;
    ok = locked = JS_FALSE;
    if (argc == 0) {
	str = cx->regExpStatics.input;
	if (!str) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_NO_INPUT,
				 JS_GetStringBytes(re->source),
				 (re->flags & JSREG_GLOB) ? "g" : "",
				 (re->flags & JSREG_FOLD) ? "i" : "");
	    goto out;
	}
    } else {
	str = js_ValueToString(cx, argv[0]);
	if (!str)
	    goto out;
	argv[0] = STRING_TO_JSVAL(str);
    }
    if (re->flags & JSREG_GLOB) {
	JS_LOCK_OBJ(cx, obj);
	locked = JS_TRUE;
	i = re->lastIndex;
    } else {
	i = 0;
    }
    ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
    if (re->flags & JSREG_GLOB)
	re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i;
out:
    if (locked)
	JS_UNLOCK_OBJ(cx, obj);
    return ok;
}

static JSBool
regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval);
}

static JSBool
regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval))
	return JS_FALSE;
    if (*rval != JSVAL_TRUE)
	*rval = JSVAL_FALSE;
    return JS_TRUE;
}

static JSFunctionSpec regexp_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   regexp_toString,        0},
#endif
    {js_toString_str,   regexp_toString,        0},
    {"compile",         regexp_compile,         1},
    {"exec",            regexp_exec,            0},
    {"test",            regexp_test,            0},
    {0}
};

static JSBool
RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    /* If not constructing, replace obj with a new RegExp object. */
    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
    }
    return regexp_compile(cx, obj, argc, argv, rval);
}

JSObject *
js_InitRegExpClass(JSContext *cx, JSObject *obj)
{
    JSObject *proto, *ctor;

    proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1,
			 regexp_props, regexp_methods,
			 regexp_static_props, NULL);

    if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
	return NULL;
    if (!JS_AliasProperty(cx, ctor, "input",        "$_") ||
	!JS_AliasProperty(cx, ctor, "multiline",    "$*") ||
	!JS_AliasProperty(cx, ctor, "lastMatch",    "$&") ||
	!JS_AliasProperty(cx, ctor, "lastParen",    "$+") ||
	!JS_AliasProperty(cx, ctor, "leftContext",  "$`") ||
	!JS_AliasProperty(cx, ctor, "rightContext", "$'")) {
	goto bad;
    }
    return proto;

bad:
    JS_DeleteProperty(cx, obj, js_RegExpClass.name);
    return NULL;
}

JSObject *
js_NewRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags)
{
    JSString *str;
    JSObject *obj;
    JSRegExp *re;

    str = js_NewStringCopyN(cx, chars, length, 0);
    if (!str)
	return NULL;
    re = js_NewRegExp(cx, str, flags);
    if (!re)
	return NULL;
    obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);
    if (!obj || !JS_SetPrivate(cx, obj, re)) {
	js_DestroyRegExp(cx, re);
	return NULL;
    }
    return obj;
}

#endif /* JS_HAS_REGEXPS */

**** End of jsregexp.c. ****

**** Start of jsregexp.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsregexp_h___
#define jsregexp_h___
/*
 * JS regular expression interface.
 */
#include <stddef.h>
#include "jspubtd.h"
#include "jsstr.h"

struct JSRegExpStatics {
    JSString    *input;         /* input string to match (perl $_, GC root) */
    JSBool      multiline;      /* whether input contains newlines (perl $*) */
    uintN       parenCount;     /* number of valid elements in parens[] */
    uintN       moreLength;     /* number of allocated elements in moreParens */
    JSSubString parens[9];      /* last set of parens matched (perl $1, $2) */
    JSSubString *moreParens;    /* null or realloc'd vector for $10, etc. */
    JSSubString lastMatch;      /* last string matched (perl $&) */
    JSSubString lastParen;      /* last paren matched (perl $+) */
    JSSubString leftContext;    /* input to left of last match (perl $`) */
    JSSubString rightContext;   /* input to right of last match (perl $') */
};

/*
 * This macro is safe because moreParens is guaranteed to be allocated and big
 * enough to hold parenCount, or else be null when parenCount is 0.
 */
#define REGEXP_PAREN_SUBSTRING(res, num)                                      \
    (((jsuint)(num) < (jsuint)(res)->parenCount)                              \
     ? ((jsuint)(num) < 9)                                                    \
       ? &(res)->parens[num]                                                  \
       : &(res)->moreParens[(num) - 9]                                        \
     : &js_EmptySubString)

struct JSRegExp {
    JSString    *source;        /* locked source string, sans // */
    size_t      length;         /* program length in bytes */
    size_t      lastIndex;      /* index after last match, for //g iterator */
    uintN       parenCount;     /* number of parenthesized submatches */
    uint8       flags;          /* flags, see jsapi.h */
    jsbytecode  program[1];     /* regular expression bytecode */
};

extern JSRegExp *
js_NewRegExp(JSContext *cx, JSString *str, uintN flags);

extern JSRegExp *
js_NewRegExpOpt(JSContext *cx, JSString *str, JSString *opt);

extern void
js_DestroyRegExp(JSContext *cx, JSRegExp *re);

/*
 * Execute re on input str at *indexp, returning null in *rval on mismatch.
 * On match, return true if test is true, otherwise return an array object.
 * Update *indexp and cx->regExpStatics always on match.
 */
extern JSBool
js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
		 JSBool test, jsval *rval);

/*
 * These two add and remove GC roots, respectively, so their calls must be
 * well-ordered.
 */
extern JSBool
js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res);

extern void
js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res);

#define JSVAL_IS_REGEXP(cx, v)                                                \
    (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) &&                              \
     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass)

extern JSClass js_RegExpClass;

extern JSObject *
js_InitRegExpClass(JSContext *cx, JSObject *obj);

/*
 * Create a new RegExp object.
 */
extern JSObject *
js_NewRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags);

extern JSBool
js_XDRRegExp(JSXDRState *xdr, JSObject **objp);

#endif /* jsregexp_h___ */

**** End of jsregexp.h. ****

**** Start of jsscan.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS lexical scanner.
 */
#include "jsstddef.h"
#include <stdio.h>	/* first to avoid trouble on some systems */
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <memory.h>
#include <stdarg.h>

#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsdtoa.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsexn.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsregexp.h"
#include "jsscan.h"

#define RESERVE_JAVA_KEYWORDS
#define RESERVE_ECMA_KEYWORDS

static struct keyword {
    char        *name;
    int16       tokentype;      /* JSTokenType */
    int8        op;             /* JSOp */
    uint8       version;        /* JSVersion */
} keywords[] = {
    {"break",           TOK_BREAK,              JSOP_NOP},
    {"case",            TOK_CASE,               JSOP_NOP},
    {"continue",        TOK_CONTINUE,           JSOP_NOP},
    {"default",         TOK_DEFAULT,            JSOP_NOP},
    {js_delete_str,     TOK_DELETE,             JSOP_NOP},
    {"do",              TOK_DO,                 JSOP_NOP},
    {"else",            TOK_ELSE,               JSOP_NOP},
    {"export",          TOK_EXPORT,             JSOP_NOP,       JSVERSION_1_2},
    {js_false_str,      TOK_PRIMARY,            JSOP_FALSE},
    {"for",             TOK_FOR,                JSOP_NOP},
    {"function",        TOK_FUNCTION,           JSOP_NOP},
    {"if",              TOK_IF,                 JSOP_NOP},
    {js_in_str,         TOK_IN,                 JSOP_IN},
    {js_new_str,        TOK_NEW,                JSOP_NEW},
    {js_null_str,       TOK_PRIMARY,            JSOP_NULL},
    {"return",          TOK_RETURN,             JSOP_NOP},
    {"switch",          TOK_SWITCH,             JSOP_NOP},
    {js_this_str,       TOK_PRIMARY,            JSOP_THIS},
    {js_true_str,       TOK_PRIMARY,            JSOP_TRUE},
    {js_typeof_str,     TOK_UNARYOP,            JSOP_TYPEOF},
    {"var",             TOK_VAR,                JSOP_NOP},
    {js_void_str,       TOK_UNARYOP,            JSOP_VOID},
    {"while",           TOK_WHILE,              JSOP_NOP},
    {"with",            TOK_WITH,               JSOP_NOP},

#if JS_HAS_EXCEPTIONS
    {"try",             TOK_TRY,                JSOP_NOP},
    {"catch",           TOK_CATCH,              JSOP_NOP},
    {"finally",         TOK_FINALLY,            JSOP_NOP},
    {"throw",           TOK_THROW,              JSOP_NOP},
#else
    {"try",             TOK_RESERVED,           JSOP_NOP},
    {"catch",           TOK_RESERVED,           JSOP_NOP},
    {"finally",         TOK_RESERVED,           JSOP_NOP},
    {"throw",           TOK_RESERVED,           JSOP_NOP},
#endif

#if JS_HAS_INSTANCEOF
    {js_instanceof_str, TOK_INSTANCEOF,         JSOP_INSTANCEOF},
#else
    {js_instanceof_str, TOK_RESERVED,           JSOP_NOP},
#endif

#ifdef RESERVE_JAVA_KEYWORDS
    {"abstract",        TOK_RESERVED,           JSOP_NOP},
    {"boolean",         TOK_RESERVED,           JSOP_NOP},
    {"byte",            TOK_RESERVED,           JSOP_NOP},
    {"char",            TOK_RESERVED,           JSOP_NOP},
    {"class",           TOK_RESERVED,           JSOP_NOP},
    {"const",           TOK_RESERVED,           JSOP_NOP},
    {"double",          TOK_RESERVED,           JSOP_NOP},
    {"extends",         TOK_RESERVED,           JSOP_NOP},
    {"final",           TOK_RESERVED,           JSOP_NOP},
    {"float",           TOK_RESERVED,           JSOP_NOP},
    {"goto",            TOK_RESERVED,           JSOP_NOP},
    {"implements",      TOK_RESERVED,           JSOP_NOP},
    {"import",          TOK_IMPORT,             JSOP_NOP},
    {"int",             TOK_RESERVED,           JSOP_NOP},
    {"interface",       TOK_RESERVED,           JSOP_NOP},
    {"long",            TOK_RESERVED,           JSOP_NOP},
    {"native",          TOK_RESERVED,           JSOP_NOP},
    {"package",         TOK_RESERVED,           JSOP_NOP},
    {"private",         TOK_RESERVED,           JSOP_NOP},
    {"protected",       TOK_RESERVED,           JSOP_NOP},
    {"public",          TOK_RESERVED,           JSOP_NOP},
    {"short",           TOK_RESERVED,           JSOP_NOP},
    {"static",          TOK_RESERVED,           JSOP_NOP},
    {"super",           TOK_PRIMARY,            JSOP_NOP},
    {"synchronized",    TOK_RESERVED,           JSOP_NOP},
    {"throws",          TOK_RESERVED,           JSOP_NOP},
    {"transient",       TOK_RESERVED,           JSOP_NOP},
    {"volatile",        TOK_RESERVED,           JSOP_NOP},
#endif

#ifdef RESERVE_ECMA_KEYWORDS
    {"enum",           TOK_RESERVED,            JSOP_NOP,       JSVERSION_1_3},
#endif

#if JS_HAS_DEBUGGER_KEYWORD
    {"debugger",       TOK_DEBUGGER,            JSOP_NOP}, /* XXX version? */
#elif defined(RESERVE_ECMA_KEYWORDS)
    {"debugger",       TOK_RESERVED,            JSOP_NOP,       JSVERSION_1_3},
#endif
    {0}
};

JSBool
js_InitScanner(JSContext *cx)
{
    struct keyword *kw;
    JSAtom *atom;

    for (kw = keywords; kw->name; kw++) {
	atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED);
	if (!atom)
	    return JS_FALSE;
	atom->kwindex = (JSVERSION_IS_ECMA(cx->version)
			 || kw->version <= cx->version) ? kw - keywords : -1;
    }
    return JS_TRUE;
}

JS_FRIEND_API(void)
js_MapKeywords(void (*mapfun)(const char *))
{
    struct keyword *kw;

    for (kw = keywords; kw->name; kw++)
	mapfun(kw->name);
}

JSTokenStream *
js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
		  const char *filename, uintN lineno,
		  JSPrincipals *principals)
{
    JSTokenStream *ts;

    ts = js_NewBufferTokenStream(cx, base, length);
    if (!ts)
	return NULL;
    ts->filename = filename;
    ts->lineno = lineno;
    if (principals)
	JSPRINCIPALS_HOLD(cx, principals);
    ts->principals = principals;
    return ts;
}

JS_FRIEND_API(JSTokenStream *)
js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)
{
    size_t nb;
    JSTokenStream *ts;

    nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);
    JS_ARENA_ALLOCATE(ts, &cx->tempPool, nb);
    if (!ts) {
	JS_ReportOutOfMemory(cx);
	return NULL;
    }
    memset(ts, 0, nb);
    CLEAR_PUSHBACK(ts);
    ts->lineno = 1;
    ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);
    ts->userbuf.base = (jschar *)base;
    ts->userbuf.limit = (jschar *)base + length;
    ts->userbuf.ptr = (jschar *)base;
    ts->listener = cx->runtime->sourceHandler;
    ts->listenerData = cx->runtime->sourceHandlerData;
    return ts;
}

#ifdef JSFILE
JS_FRIEND_API(JSTokenStream *)
js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)
{
    jschar *base;
    JSTokenStream *ts;
    FILE *file;

    JS_ARENA_ALLOCATE(base, &cx->tempPool, JS_LINE_LIMIT * sizeof(jschar));
    if (!base)
	return NULL;
    ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);
    if (!ts)
	return NULL;
    if (!filename || strcmp(filename, "-") == 0) {
	file = defaultfp;
    } else {
	file = fopen(filename, "r");
	if (!file) {
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
				 filename, strerror(errno));
	    return NULL;
	}
    }
    ts->userbuf.ptr = ts->userbuf.limit;
    ts->file = file;
    ts->filename = filename;
    return ts;
}
#endif /* JSFILE */

JS_FRIEND_API(JSBool)
js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)
{
    if (ts->principals)
	JSPRINCIPALS_DROP(cx, ts->principals);
#ifdef JSFILE
    return !ts->file || fclose(ts->file) == 0;
#else
    return JS_TRUE;
#endif
}

static int32
GetChar(JSTokenStream *ts)
{
    int32 c;
    ptrdiff_t len, olen;
    jschar *nl;

    if (ts->ungetpos != 0) {
	c = ts->ungetbuf[--ts->ungetpos];
    } else {
	if (ts->linebuf.ptr == ts->linebuf.limit) {
	    len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);
	    if (len <= 0) {
#ifdef JSFILE
		/* Fill ts->userbuf so that \r and \r\n convert to \n. */
		if (ts->file) {
		    JSBool crflag;
		    char cbuf[JS_LINE_LIMIT];
		    jschar *ubuf;
		    ptrdiff_t i, j;

		    crflag = (ts->flags & TSF_CRFLAG) != 0;
		    if (!fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file)) {
			ts->flags |= TSF_EOF;
			return EOF;
		    }
		    len = olen = strlen(cbuf);
		    JS_ASSERT(len > 0);
		    ubuf = ts->userbuf.base;
		    i = 0;
		    if (crflag) {
			ts->flags &= ~TSF_CRFLAG;
			if (cbuf[0] != '\n') {
			    ubuf[i++] = '\n';
			    len++;
			    ts->linepos--;
			}
		    }
		    for (j = 0; i < len; i++, j++)
			ubuf[i] = (jschar) (unsigned char) cbuf[j];
		    ts->userbuf.limit = ubuf + len;
		    ts->userbuf.ptr = ubuf;
		} else
#endif /* JSFILE */
		{
		    ts->flags |= TSF_EOF;
		    return EOF;
		}
	    }
            if (ts->listener)
                (*ts->listener)(ts->filename, ts->lineno, ts->userbuf.ptr, len,
                                &ts->listenerTSData, ts->listenerData);
	    /*
	     * Any one of \n, \r, or \r\n ends a line (longest match wins).
	     */
	    for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {
#if 1
		if (*nl == '\n') {
		    if (nl + 1 < ts->userbuf.limit && nl[1] == '\r')
				nl++;
		    break;
		}
#else
		if (*nl == '\n')
		    break;
#endif
		if (*nl == '\r') {
		    if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')
			nl++;
		    break;
		}
	    }

	    /*
	     * If there was a line terminator, copy thru it into linebuf.
	     * Else copy JS_LINE_LIMIT-1 bytes into linebuf.
	     */
	    if (nl < ts->userbuf.limit)
		len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;
	    if (len >= JS_LINE_LIMIT)
		len = JS_LINE_LIMIT - 1;
	    js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);
	    ts->userbuf.ptr += len;
	    olen = len;

	    /*
	     * Make sure linebuf contains \n for EOL (don't do this in
	     * userbuf because the user's string might be readonly).
	     */
	    if (nl < ts->userbuf.limit) {
		if (*nl == '\r') {
		    if (ts->linebuf.base[len-1] == '\r') {
#ifdef JSFILE
			/*
			 * Does the line segment end in \r?  We must check
			 * for a \n at the front of the next segment before
			 * storing a \n into linebuf.
			 */
			if (nl + 1 == ts->userbuf.limit) {
			    len--;
			    ts->flags |= TSF_CRFLAG;
			} else
#endif
			ts->linebuf.base[len-1] = '\n';
		    }
		} else if (*nl == '\n') {
		    if (nl > ts->userbuf.base &&
			nl[-1] == '\r' &&
			ts->linebuf.base[len-2] == '\r') {
			len--;
			JS_ASSERT(ts->linebuf.base[len] == '\n');
			ts->linebuf.base[len-1] = '\n';
		    }
		}
	    }

	    /* Reset linebuf based on adjusted segment length. */
	    ts->linebuf.limit = ts->linebuf.base + len;
	    ts->linebuf.ptr = ts->linebuf.base;

	    /* Update position of linebuf within physical line in userbuf. */
	    if (!(ts->flags & TSF_NLFLAG))
		ts->linepos += ts->linelen;
	    else
		ts->linepos = 0;
	    if (ts->linebuf.limit[-1] == '\n')
		ts->flags |= TSF_NLFLAG;
	    else
		ts->flags &= ~TSF_NLFLAG;

	    /* Update linelen from original segment length. */
	    ts->linelen = olen;
	}
	c = *ts->linebuf.ptr++;
    }
    if (c == '\n')
	ts->lineno++;
    return c;
}

static void
UngetChar(JSTokenStream *ts, int32 c)
{
    if (c == EOF)
	return;
    JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);
    if (c == '\n')
	ts->lineno--;
    ts->ungetbuf[ts->ungetpos++] = (jschar)c;
}

static int32
PeekChar(JSTokenStream *ts)
{
    int32 c;

    c = GetChar(ts);
    UngetChar(ts, c);
    return c;
}

static JSBool
PeekChars(JSTokenStream *ts, intN n, jschar *cp)
{
    intN i, j;
    int32 c;

    for (i = 0; i < n; i++) {
	c = GetChar(ts);
	if (c == EOF)
	    break;
	cp[i] = (jschar)c;
    }
    for (j = i - 1; j >= 0; j--)
	UngetChar(ts, cp[j]);
    return i == n;
}

static void
SkipChars(JSTokenStream *ts, intN n)
{
    while (--n >= 0)
	GetChar(ts);
}

static JSBool
MatchChar(JSTokenStream *ts, int32 expect)
{
    int32 c;

    c = GetChar(ts);
    if (c == expect)
	return JS_TRUE;
    UngetChar(ts, c);
    return JS_FALSE;
}

#if 0
/* XXX js_ReportCompileError is unused */
void
js_ReportCompileError(JSContext *cx, JSTokenStream *ts, uintN flags,
		      const char *format, ...)
{
    va_list ap;
    char *message;
    jschar *limit, lastc;
    JSErrorReporter onError;
    JSErrorReport report;
    JSString *linestr;

    va_start(ap, format);
    message = JS_vsmprintf(format, ap);
    va_end(ap);
    if (!message) {
	JS_ReportOutOfMemory(cx);
	return;
    }

    JS_ASSERT(ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
    limit = ts->linebuf.limit;
    lastc = limit[-1];
    if (lastc == '\n')
	limit[-1] = 0;
    onError = cx->errorReporter;
    if (onError) {
	report.filename = ts->filename;
	report.lineno = ts->lineno;
	linestr = js_NewStringCopyZ(cx, ts->linebuf.base, 0);
	report.linebuf  = linestr
			  ? JS_GetStringBytes(linestr)
			  : NULL;
	report.tokenptr = linestr
			  ? report.linebuf + (ts->token.ptr - ts->linebuf.base)
			  : NULL;
	report.uclinebuf = ts->linebuf.base;
	report.uctokenptr = ts->token.ptr;
	report.flags = flags;
        report.errorNumber = 0;

	(*onError)(cx, message, &report);
#if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800
    } else {
	if (!(ts->flags & TSF_INTERACTIVE))
	    fprintf(stderr, "JavaScript %s: ",
		    JSREPORT_IS_WARNING(flags) ? "warning" : "error");
	if (ts->filename)
	    fprintf(stderr, "%s, ", ts->filename);
	if (ts->lineno)
	    fprintf(stderr, "line %u: ", ts->lineno);
	fprintf(stderr, "%s:\n%s\n",message,
		js_DeflateString(cx, ts->linebuf.base,
				 ts->linebuf.limit - ts->linebuf.base));
#endif
    }
    if (lastc == '\n')
	limit[-1] = lastc;
    free(message);
}
#endif

void
js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, uintN flags,
			    const uintN errorNumber, ...)
{
    va_list ap;
    jschar *limit, lastc;
    JSErrorReporter onError;
    JSErrorReport report;
    JSString *linestr;
    char *message;

    report.errorNumber = errorNumber;
    report.messageArgs = NULL;
    report.ucmessage = NULL;
    message = NULL;

    va_start(ap, errorNumber);
    if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,
				errorNumber, &message, &report,
                                JS_TRUE, ap))
	return;
    va_end(ap);

    JS_ASSERT(ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);
    limit = ts->linebuf.limit;
    lastc = limit[-1];
    if (lastc == '\n')
	limit[-1] = 0;
    onError = cx->errorReporter;
    if (onError) {
	report.filename = ts->filename;
	report.lineno = ts->lineno;
	linestr = js_NewStringCopyZ(cx, ts->linebuf.base, 0);
	report.linebuf  = linestr
			  ? JS_GetStringBytes(linestr)
			  : NULL;
	report.tokenptr = linestr
			  ? report.linebuf + (ts->token.ptr - ts->linebuf.base)
			  : NULL;
	report.uclinebuf = ts->linebuf.base;
	report.uctokenptr = ts->token.ptr;
	report.flags = flags;

#if JS_HAS_ERROR_EXCEPTIONS
	/*
	 * If there's a runtime exception type associated with this error
	 * number, set that as the pending exception.  For errors occuring at
	 * compile time, this is very likely to be a JSEXN_SYNTAXERR.  If an
	 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set in
	 * report.flags.  Proper behavior for error reporters is probably to
	 * ignore this for all but toplevel compilation errors.

	 * XXX it'd probably be best if there was only one call to this
	 * function, but there seem to be two error reporter call points.
	 */

        /*
         * Only try to raise an exception if there isn't one already set -
         * otherwise the exception will describe only the last compile error,
         * which is likely spurious.
         */
        if (!(ts->flags & TSF_BADCOMPILE))
            if (js_ErrorToException(cx, message, &report)) {
                ts->flags |= TSF_BADCOMPILE;
            }

        /*
         * Suppress any compiletime errors that don't occur at the top level.
         * This may still fail, as interplevel may be zero in contexts where we
         * don't really want to call the error reporter, as when js is called
         * by other code which could catch the error.
         */
        if (cx->interpLevel != 0)
            onError = NULL;
#endif
        if (cx->runtime->debugErrorHook && onError) {
            JSDebugErrorHook hook = cx->runtime->debugErrorHook;
            /* test local in case debugErrorHook changed on another thread */
            if (hook && !hook(cx, message, &report,
                              cx->runtime->debugErrorHookData)) {
                onError = NULL;
            }
        }
        if (onError)
            (*onError)(cx, message, &report);

#if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800
    } else {
	if (!(ts->flags & TSF_INTERACTIVE))
	    fprintf(stderr, "JavaScript %s: ",
		    JSREPORT_IS_WARNING(flags) ? "warning" : "error");
	if (ts->filename)
	    fprintf(stderr, "%s, ", ts->filename);
	if (ts->lineno)
	    fprintf(stderr, "line %u: ", ts->lineno);
	fprintf(stderr, "%s:\n%s\n",message,
		js_DeflateString(cx, ts->linebuf.base,
				 ts->linebuf.limit - ts->linebuf.base));
#endif
    }
    if (lastc == '\n')
	limit[-1] = lastc;
    if (message) JS_free(cx, message);
    if (report.messageArgs) JS_free(cx, (void *)report.messageArgs);
}

JSTokenType
js_PeekToken(JSContext *cx, JSTokenStream *ts)
{
    JSTokenType tt;

    tt = ts->pushback.type;
    if (tt == TOK_EOF) {
	tt = js_GetToken(cx, ts);
	js_UngetToken(ts);
    }
    return tt;
}

JSTokenType
js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)
{
    uintN newlines;
    JSTokenType tt;

    newlines = ts->flags & TSF_NEWLINES;
    if (!newlines)
	SCAN_NEWLINES(ts);
    tt = js_PeekToken(cx, ts);
    if (!newlines)
	HIDE_NEWLINES(ts);
    return tt;
}

#define TBINCR         64

static JSBool
GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)
{
    jschar *base;
    ptrdiff_t offset, length;
    size_t tbincr, tbsize;
    JSArenaPool *pool;

    base = tb->base;
    offset = PTRDIFF(tb->ptr, base, jschar);
    length = PTRDIFF(tb->limit, base, jschar);
    tbincr = TBINCR * sizeof(jschar);
    pool = &cx->tempPool;
    if (!base) {
	JS_ARENA_ALLOCATE(base, pool, tbincr);
    } else {
	tbsize = (size_t)(length * sizeof(jschar));
	JS_ARENA_GROW(base, pool, tbsize, tbincr);
    }
    if (!base) {
	JS_ReportOutOfMemory(cx);
	return JS_FALSE;
    }
    tb->base = base;
    tb->limit = base + length + TBINCR;
    tb->ptr = base + offset;
    return JS_TRUE;
}

static JSBool
AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)
{
    if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))
	return JS_FALSE;
    *tb->ptr++ = c;
    return JS_TRUE;
}

JSTokenType
js_GetToken(JSContext *cx, JSTokenStream *ts)
{
    int32 c;
    JSAtom *atom;

#define INIT_TOKENBUF(tb)   ((tb)->ptr = (tb)->base)
#define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)
#define TOKEN_LENGTH(tb)    ((tb)->ptr - (tb)->base - 1)
#define RETURN(tt)          { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR;    \
			      ts->token.pos.end.index = ts->linepos +         \
				  (ts->linebuf.ptr - ts->linebuf.base) -      \
				  ts->ungetpos;                               \
			      return (ts->token.type = tt); }

    /* If there was a fatal error, keep returning TOK_ERROR. */
    if (ts->flags & TSF_ERROR)
	return TOK_ERROR;

    /* Check for a pushed-back token resulting from mismatching lookahead. */
    if (ts->pushback.type != TOK_EOF) {
	ts->token = ts->pushback;
	CLEAR_PUSHBACK(ts);
	return ts->token.type;
    }

retry:
    do {
	c = GetChar(ts);
	if (c == '\n' && (ts->flags & TSF_NEWLINES))
	    break;
    } while (JS_ISSPACE(c));

    ts->token.ptr = ts->linebuf.ptr - 1;
    ts->token.pos.begin.index = ts->linepos + (ts->token.ptr-ts->linebuf.base);
    ts->token.pos.begin.lineno = ts->token.pos.end.lineno = ts->lineno;

    if (c == EOF)
	RETURN(TOK_EOF);

    if (JS_ISIDENT(c)) {
	INIT_TOKENBUF(&ts->tokenbuf);
	do {
	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		RETURN(TOK_ERROR);
	    c = GetChar(ts);
	} while (JS_ISIDENT2(c));
	UngetChar(ts, c);
	FINISH_TOKENBUF(&ts->tokenbuf);

	atom = js_AtomizeChars(cx,
			       ts->tokenbuf.base,
			       TOKEN_LENGTH(&ts->tokenbuf),
			       0);
	if (!atom)
	    RETURN(TOK_ERROR);
	if (atom->kwindex >= 0) {
	    struct keyword *kw;

	    kw = &keywords[atom->kwindex];
	    ts->token.t_op = kw->op;
	    RETURN(kw->tokentype);
	}
	ts->token.t_op = JSOP_NAME;
	ts->token.t_atom = atom;
	RETURN(TOK_NAME);
    }

    if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {
	jsint radix;
	const jschar *endptr;
	jsdouble dval;

	radix = 10;
	INIT_TOKENBUF(&ts->tokenbuf);

	if (c == '0') {
	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		RETURN(TOK_ERROR);
	    c = GetChar(ts);
	    if (JS_TOLOWER(c) == 'x') {
		if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		    RETURN(TOK_ERROR);
		c = GetChar(ts);
		radix = 16;
	    } else if (JS7_ISDEC(c)) {
		/*
		 * We permit 08 and 09 as decimal numbers, which makes our
		 * behaviour a superset of the ECMA numeric grammar.  We might
		 * not always be so permissive, so we warn about it.
		 */
		if (c > '7' && JSVERSION_IS_ECMA(cx->version)) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_WARNING,
                                                JSMSG_BAD_OCTAL,
                                                c == '8' ? "08" : "09");
		    radix = 10;
		} else {
		    radix = 8;
		}
	    }
	}

	while (JS7_ISHEX(c)) {
	    if (radix < 16 && (JS7_ISLET(c) || (radix == 8 && c >= '8')))
		break;
	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		RETURN(TOK_ERROR);
	    c = GetChar(ts);
	}

	if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
	    if (c == '.') {
		do {
		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
			RETURN(TOK_ERROR);
		    c = GetChar(ts);
		} while (JS7_ISDEC(c));
	    }
	    if (JS_TOLOWER(c) == 'e') {
		if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		    RETURN(TOK_ERROR);
		c = GetChar(ts);
		if (c == '+' || c == '-') {
		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
			RETURN(TOK_ERROR);
		    c = GetChar(ts);
		}
		if (!JS7_ISDEC(c)) {
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                                JSMSG_MISSING_EXPONENT);
		    RETURN(TOK_ERROR);
		}
		do {
		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
			RETURN(TOK_ERROR);
		    c = GetChar(ts);
		} while (JS7_ISDEC(c));
	    }
	}

	UngetChar(ts, c);
	FINISH_TOKENBUF(&ts->tokenbuf);

	if (radix == 10) {
	    if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) {
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                            JSMSG_OUT_OF_MEMORY);
		RETURN(TOK_ERROR);
	    }
	} else {
	    if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) {
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                            JSMSG_OUT_OF_MEMORY);
		RETURN(TOK_ERROR);
	    }
	}
	ts->token.t_dval = dval;
	RETURN(TOK_NUMBER);
    }

    if (c == '"' || c == '\'') {
	int32 val, qc = c;

	INIT_TOKENBUF(&ts->tokenbuf);
	while ((c = GetChar(ts)) != qc) {
	    if (c == '\n' || c == EOF) {
		UngetChar(ts, c);
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                            JSMSG_UNTERMINATED_STRING);
		RETURN(TOK_ERROR);
	    }
	    if (c == '\\') {
		switch (c = GetChar(ts)) {
		  case 'b': c = '\b'; break;
		  case 'f': c = '\f'; break;
		  case 'n': c = '\n'; break;
		  case 'r': c = '\r'; break;
		  case 't': c = '\t'; break;
		  case 'v': c = '\v'; break;

		  default:
		    if ('0' <= c && c < '8') {
			val = JS7_UNDEC(c);
			c = PeekChar(ts);
			if ('0' <= c && c < '8') {
			    val = 8 * val + JS7_UNDEC(c);
			    GetChar(ts);
			    c = PeekChar(ts);
			    if ('0' <= c && c < '8') {
				int32 save = val;
				val = 8 * val + JS7_UNDEC(c);
				if (val <= 0377)
				    GetChar(ts);
				else
				    val = save;
			    }
			}
			c = (jschar)val;
		    } else if (c == 'u') {
			jschar cp[4];
			if (PeekChars(ts, 4, cp) &&
			    JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&
			    JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {
			    c = (((((JS7_UNHEX(cp[0]) << 4)
				    + JS7_UNHEX(cp[1])) << 4)
				  + JS7_UNHEX(cp[2])) << 4)
				+ JS7_UNHEX(cp[3]);
			    SkipChars(ts, 4);
			}
		    } else if (c == 'x') {
			jschar cp[2];
			if (PeekChars(ts, 2, cp) &&
			    JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {
			    c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
			    SkipChars(ts, 2);
			}
		    }
		    break;
		}
	    }
	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		RETURN(TOK_ERROR);
	}
	FINISH_TOKENBUF(&ts->tokenbuf);
	atom = js_AtomizeChars(cx,
			       ts->tokenbuf.base,
			       TOKEN_LENGTH(&ts->tokenbuf),
			       0);
	if (!atom)
	    RETURN(TOK_ERROR);
	ts->token.pos.end.lineno = ts->lineno;
	ts->token.t_op = JSOP_STRING;
	ts->token.t_atom = atom;
	RETURN(TOK_STRING);
    }

    switch (c) {
      case '\n': c = TOK_EOL; break;
      case ';': c = TOK_SEMI; break;
      case '.': c = TOK_DOT; break;
      case '[': c = TOK_LB; break;
      case ']': c = TOK_RB; break;
      case '{': c = TOK_LC; break;
      case '}': c = TOK_RC; break;
      case '(': c = TOK_LP; break;
      case ')': c = TOK_RP; break;
      case ',': c = TOK_COMMA; break;
      case '?': c = TOK_HOOK; break;
      case ':': c = TOK_COLON; break;

      case '|':
	if (MatchChar(ts, c)) {
	    c = TOK_OR;
	} else if (MatchChar(ts, '=')) {
	    ts->token.t_op = JSOP_BITOR;
	    c = TOK_ASSIGN;
	} else {
	    c = TOK_BITOR;
	}
	break;

      case '^':
	if (MatchChar(ts, '=')) {
	    ts->token.t_op = JSOP_BITXOR;
	    c = TOK_ASSIGN;
	} else {
	    c = TOK_BITXOR;
	}
	break;

      case '&':
	if (MatchChar(ts, c)) {
	    c = TOK_AND;
	} else if (MatchChar(ts, '=')) {
	    ts->token.t_op = JSOP_BITAND;
	    c = TOK_ASSIGN;
	} else {
	    c = TOK_BITAND;
	}
	break;

      case '=':
	if (MatchChar(ts, c)) {
#if JS_HAS_TRIPLE_EQOPS
	    ts->token.t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : cx->jsop_eq;
#else
	    ts->token.t_op = cx->jsop_eq;
#endif
	    c = TOK_EQOP;
	} else {
	    ts->token.t_op = JSOP_NOP;
	    c = TOK_ASSIGN;
	}
	break;

      case '!':
	if (MatchChar(ts, '=')) {
#if JS_HAS_TRIPLE_EQOPS
	    ts->token.t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : cx->jsop_ne;
#else
	    ts->token.t_op = cx->jsop_ne;
#endif
	    c = TOK_EQOP;
	} else {
	    ts->token.t_op = JSOP_NOT;
	    c = TOK_UNARYOP;
	}
	break;

      case '<':
	/* NB: treat HTML begin-comment as comment-till-end-of-line */
	if (MatchChar(ts, '!')) {
	    if (MatchChar(ts, '-')) {
		if (MatchChar(ts, '-'))
		    goto skipline;
		UngetChar(ts, '-');
	    }
	    UngetChar(ts, '!');
	}
	if (MatchChar(ts, c)) {
	    ts->token.t_op = JSOP_LSH;
	    c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
	} else {
	    ts->token.t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;
	    c = TOK_RELOP;
	}
	break;

      case '>':
	if (MatchChar(ts, c)) {
	    ts->token.t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;
	    c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;
	} else {
	    ts->token.t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;
	    c = TOK_RELOP;
	}
	break;

      case '*':
	ts->token.t_op = JSOP_MUL;
	c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;
	break;

      case '/':
	if (MatchChar(ts, '/')) {
skipline:
	    while ((c = GetChar(ts)) != EOF && c != '\n')
		/* skip to end of line */;
	    UngetChar(ts, c);
	    goto retry;
	}
	if (MatchChar(ts, '*')) {
	    while ((c = GetChar(ts)) != EOF &&
		   !(c == '*' && MatchChar(ts, '/'))) {
		if (c == '/' && MatchChar(ts, '*')) {
		    if (MatchChar(ts, '/'))
			goto retry;
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                                JSMSG_NESTED_COMMENT);
		}
	    }
	    if (c == EOF) {
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                            JSMSG_UNTERMINATED_COMMENT);
		RETURN(TOK_ERROR);
	    }
	    goto retry;
	}

#if JS_HAS_REGEXPS
	if (ts->flags & TSF_REGEXP) {
	    JSObject *obj;
	    uintN flags;

	    INIT_TOKENBUF(&ts->tokenbuf);
	    while ((c = GetChar(ts)) != '/') {
		if (c == '\n' || c == EOF) {
		    UngetChar(ts, c);
		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                                JSMSG_UNTERMINATED_REGEXP);
		    RETURN(TOK_ERROR);
		}
		if (c == '\\') {
		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
			RETURN(TOK_ERROR);
		    c = GetChar(ts);
		}
		if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))
		    RETURN(TOK_ERROR);
	    }
	    FINISH_TOKENBUF(&ts->tokenbuf);
	    for (flags = 0; ; ) {
		if (MatchChar(ts, 'g'))
		    flags |= JSREG_GLOB;
		else if (MatchChar(ts, 'i'))
		    flags |= JSREG_FOLD;
		else
		    break;
	    }
	    c = PeekChar(ts);
	    if (JS7_ISLET(c)) {
		ts->token.ptr = ts->linebuf.ptr - 1;
		js_ReportCompileErrorNumber(cx, ts,JSREPORT_ERROR,
                                            JSMSG_BAD_REGEXP_FLAG);
		(void) GetChar(ts);
		RETURN(TOK_ERROR);
	    }
	    obj = js_NewRegExpObject(cx,
				     ts->tokenbuf.base,
				     TOKEN_LENGTH(&ts->tokenbuf),
				     flags);
	    if (!obj)
		RETURN(TOK_ERROR);
	    atom = js_AtomizeObject(cx, obj, 0);
	    if (!atom)
		RETURN(TOK_ERROR);
	    ts->token.t_op = JSOP_OBJECT;
	    ts->token.t_atom = atom;
	    RETURN(TOK_OBJECT);
	}
#endif /* JS_HAS_REGEXPS */

	ts->token.t_op = JSOP_DIV;
	c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
	break;

      case '%':
	ts->token.t_op = JSOP_MOD;
	c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;
	break;

      case '~':
	ts->token.t_op = JSOP_BITNOT;
	c = TOK_UNARYOP;
	break;

      case '+':
      case '-':
	if (MatchChar(ts, '=')) {
	    ts->token.t_op = (c == '+') ? JSOP_ADD : JSOP_SUB;
	    c = TOK_ASSIGN;
	} else if (MatchChar(ts, c)) {
	    c = (c == '+') ? TOK_INC : TOK_DEC;
	} else if (c == '-') {
	    ts->token.t_op = JSOP_NEG;
	    c = TOK_MINUS;
	} else {
	    ts->token.t_op = JSOP_POS;
	    c = TOK_PLUS;
	}
	break;

#if JS_HAS_SHARP_VARS
      case '#':
      {
	uint32 n;

	c = GetChar(ts);
	if (!JS7_ISDEC(c)) {
	    UngetChar(ts, c);
	    goto badchar;
	}
	n = (uint32)JS7_UNDEC(c);
	for (;;) {
	    c = GetChar(ts);
	    if (!JS7_ISDEC(c))
		break;
	    n = 10 * n + JS7_UNDEC(c);
	    if (n >= ATOM_INDEX_LIMIT) {
		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                            JSMSG_SHARPVAR_TOO_BIG);
		RETURN(TOK_ERROR);
	    }
	}
	ts->token.t_dval = (jsdouble) n;
	if (c == '=')
	    RETURN(TOK_DEFSHARP);
	if (c == '#')
	    RETURN(TOK_USESHARP);
	goto badchar;
      }

      badchar:
#endif /* JS_HAS_SHARP_VARS */

      default:
	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,
                                    JSMSG_ILLEGAL_CHARACTER);
	RETURN(TOK_ERROR);
    }

    JS_ASSERT(c < TOK_LIMIT);
    RETURN(c);

#undef INIT_TOKENBUF
#undef FINISH_TOKENBUF
#undef TOKEN_LENGTH
#undef RETURN
}

void
js_UngetToken(JSTokenStream *ts)
{
    JS_ASSERT(ts->pushback.type == TOK_EOF);
    if (ts->flags & TSF_ERROR)
	return;
    ts->pushback = ts->token;
}

JSBool
js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
{
    if (js_GetToken(cx, ts) == tt)
	return JS_TRUE;
    js_UngetToken(ts);
    return JS_FALSE;
}

**** End of jsscan.c. ****

**** Start of jsscan.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsscan_h___
#define jsscan_h___
/*
 * JS lexical scanner interface.
 */
#include <stddef.h>
#ifdef JSFILE
#include <stdio.h>
#endif
#include "jsopcode.h"
#include "jsprvtd.h"
#include "jspubtd.h"

JS_BEGIN_EXTERN_C

typedef enum JSTokenType {
    TOK_ERROR = -1,                     /* well-known as the only code < EOF */
    TOK_EOF = 0,                        /* end of file */
    TOK_EOL = 1,                        /* end of line */
    TOK_SEMI = 2,                       /* semicolon */
    TOK_LB = 3, TOK_RB = 4,             /* left and right brackets */
    TOK_LC = 5, TOK_RC = 6,             /* left and right curlies (braces) */
    TOK_LP = 7, TOK_RP = 8,             /* left and right parentheses */
    TOK_COMMA = 9,                      /* comma operator */
    TOK_ASSIGN = 10,                    /* assignment ops (= += -= etc.) */
    TOK_HOOK = 11, TOK_COLON = 12,      /* conditional (?:) */
    TOK_OR = 13,                        /* logical or (||) */
    TOK_AND = 14,                       /* logical and (&&) */
    TOK_BITOR = 15,                     /* bitwise-or (|) */
    TOK_BITXOR = 16,                    /* bitwise-xor (^) */
    TOK_BITAND = 17,                    /* bitwise-and (&) */
    TOK_EQOP = 18,                      /* equality ops (== !=) */
    TOK_RELOP = 19,                     /* relational ops (< <= > >=) */
    TOK_SHOP = 20,                      /* shift ops (<< >> >>>) */
    TOK_PLUS = 21,                      /* plus */
    TOK_MINUS = 22,                     /* minus */
    TOK_STAR = 23, TOK_DIVOP = 24,      /* multiply/divide ops (* / %) */
    TOK_UNARYOP = 25,                   /* unary prefix operator */
    TOK_INC = 26, TOK_DEC = 27,         /* increment/decrement (++ --) */
    TOK_DOT = 28,                       /* member operator (.) */
    TOK_NAME = 29,                      /* identifier */
    TOK_NUMBER = 30,                    /* numeric constant */
    TOK_STRING = 31,                    /* string constant */
    TOK_OBJECT = 32,                    /* RegExp or other object constant */
    TOK_PRIMARY = 33,                   /* true, false, null, this, super */
    TOK_FUNCTION = 34,                  /* function keyword */
    TOK_EXPORT = 35,                    /* export keyword */
    TOK_IMPORT = 36,                    /* import keyword */
    TOK_IF = 37,                        /* if keyword */
    TOK_ELSE = 38,                      /* else keyword */
    TOK_SWITCH = 39,                    /* switch keyword */
    TOK_CASE = 40,                      /* case keyword */
    TOK_DEFAULT = 41,                   /* default keyword */
    TOK_WHILE = 42,                     /* while keyword */
    TOK_DO = 43,                        /* do keyword */
    TOK_FOR = 44,                       /* for keyword */
    TOK_BREAK = 45,                     /* break keyword */
    TOK_CONTINUE = 46,                  /* continue keyword */
    TOK_IN = 47,                        /* in keyword */
    TOK_VAR = 48,                       /* var keyword */
    TOK_WITH = 49,                      /* with keyword */
    TOK_RETURN = 50,                    /* return keyword */
    TOK_NEW = 51,                       /* new keyword */
    TOK_DELETE = 52,                    /* delete keyword */
    TOK_DEFSHARP = 53,                  /* #n= for object/array initializers */
    TOK_USESHARP = 54,                  /* #n# for object/array initializers */
    TOK_TRY = 55,                       /* try keyword */
    TOK_CATCH = 56,                     /* catch keyword */
    TOK_FINALLY = 57,                   /* finally keyword */
    TOK_THROW = 58,                     /* throw keyword */
    TOK_INSTANCEOF = 59,                /* instanceof keyword */
    TOK_DEBUGGER = 60,                  /* debugger keyword */
    TOK_RESERVED,                       /* reserved keywords */
    TOK_LIMIT                           /* domain size */
} JSTokenType;

#define IS_PRIMARY_TOKEN(tt) \
    ((uintN)((tt) - TOK_NAME) <= (uintN)(TOK_PRIMARY - TOK_NAME))

struct JSTokenPtr {
    uint16              index;          /* index of char in physical line */
    uint16              lineno;         /* physical line number */
};

struct JSTokenPos {
    JSTokenPtr          begin;          /* first character and line of token */
    JSTokenPtr          end;            /* index 1 past last char, last line */
};

struct JSToken {
    JSTokenType         type;           /* char value or above enumerator */
    JSTokenPos          pos;            /* token position in file */
    jschar              *ptr;           /* beginning of token in line buffer */
    union {
	struct {
	    JSOp        op;             /* operator, for minimal parser */
	    JSAtom      *atom;          /* atom table entry */
	} s;
	jsdouble        dval;           /* floating point number */
    } u;
};

#define t_op            u.s.op
#define t_atom          u.s.atom
#define t_dval          u.dval

typedef struct JSTokenBuf {
    jschar              *base;          /* base of line or stream buffer */
    jschar              *limit;         /* limit for quick bounds check */
    jschar              *ptr;           /* next char to get, or slot to use */
} JSTokenBuf;

#define JS_LINE_LIMIT   256             /* logical line buffer size limit --
					   physical line length is unlimited */

struct JSTokenStream {
    JSToken             token;          /* last token scanned */
    JSToken             pushback;       /* pushed-back already-scanned token */
    uintN               lineno;         /* current line number */
    uintN               ungetpos;       /* next free char slot in ungetbuf */
    jschar              ungetbuf[4];    /* at most 4, for \uXXXX lookahead */
    uintN               flags;          /* flags -- see below */
    ptrdiff_t           linelen;        /* physical linebuf segment length */
    ptrdiff_t           linepos;        /* linebuf offset in physical line */
    JSTokenBuf          linebuf;        /* line buffer for diagnostics */
    JSTokenBuf          userbuf;        /* user input buffer if !file */
    JSTokenBuf          tokenbuf;       /* current token string buffer */
    const char          *filename;      /* input filename or null */
#ifdef JSFILE
    FILE                *file;          /* stdio stream if reading from file */
#endif
    JSPrincipals        *principals;    /* principals associated with given input */
    JSSourceHandler     listener;       /* callback for source; eg debugger */
    void                *listenerData;  /* listener 'this' data */
    void                *listenerTSData;/* listener data for this TokenStream */
};

/* JSTokenStream flags */
#define TSF_ERROR       0x01            /* fatal error while scanning */
#define TSF_EOF         0x02            /* hit end of file */
#define TSF_NEWLINES    0x04            /* tokenize newlines */
#define TSF_REGEXP      0x08            /* looking for a regular expression */
#define TSF_INTERACTIVE 0x10            /* interactive parsing mode */
#define TSF_NLFLAG      0x20            /* last linebuf ended with \n */
#define TSF_CRFLAG      0x40            /* linebuf would have ended with \r */
#define TSF_BADCOMPILE  0x80            /* compile failed, stop throwing exns */ 

/*
 * At most one non-EOF token can be pushed back onto a TokenStream between
 * Get or successful Match operations.  These macros manipulate the pushback
 * token to clear it when changing scanning modes (upon initialzation, after
 * errors, or between newline-sensitive and insensitive states).
 */
#define CLEAR_PUSHBACK(ts)  ((ts)->pushback.type = TOK_EOF)
#define SCAN_NEWLINES(ts)   ((ts)->flags |= TSF_NEWLINES)
#define HIDE_NEWLINES(ts)                                                     \
    JS_BEGIN_MACRO                                                            \
	(ts)->flags &= ~TSF_NEWLINES;                                         \
	if ((ts)->pushback.type == TOK_EOL)                                   \
	    (ts)->pushback.type = TOK_EOF;                                    \
    JS_END_MACRO

/* Clear ts member that might point above a released cx->tempPool mark. */
#define RESET_TOKENBUF(ts)  ((ts)->tokenbuf.base = (ts)->tokenbuf.limit = NULL)

/*
 * Create a new token stream, either from an input buffer or from a file.
 * Return null on file-open or memory-allocation failure.
 *
 * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient
 * memory in the current context's temp pool.  This memory is deallocated via
 * JS_ARENA_RELEASE() after parsing is finished.
 */
extern JSTokenStream *
js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,
		  const char *filename, uintN lineno, JSPrincipals *principals);

extern JS_FRIEND_API(JSTokenStream *)
js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length);

#ifdef JSFILE
extern JS_FRIEND_API(JSTokenStream *)
js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp);
#endif

extern JS_FRIEND_API(JSBool)
js_CloseTokenStream(JSContext *cx, JSTokenStream *ts);

/*
 * Initialize the scanner, installing JS keywords into cx's global scope.
 */
extern JSBool
js_InitScanner(JSContext *cx);

/*
 * Friend-exported API entry point to call a mapping function on each reserved
 * identifier in the scanner's keyword table.
 */
extern JS_FRIEND_API(void)
js_MapKeywords(void (*mapfun)(const char *));

/*
 * Report an error found while scanning ts to a window or other output device
 * associated with cx.
 */
#if 0
/* XXX js_ReportCompileError is unused */
extern void
js_ReportCompileError(JSContext *cx, JSTokenStream *ts, uintN flags,
		      const char *format, ...);
#endif

void
js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, uintN flags,
		      const uintN errorNumber, ...);

/*
 * Look ahead one token and return its type.
 */
extern JSTokenType
js_PeekToken(JSContext *cx, JSTokenStream *ts);

extern JSTokenType
js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts);

/*
 * Get the next token from ts.
 */
extern JSTokenType
js_GetToken(JSContext *cx, JSTokenStream *ts);

/*
 * Push back the last scanned token onto ts.
 */
extern void
js_UngetToken(JSTokenStream *ts);

/*
 * Get the next token from ts if its type is tt.
 */
extern JSBool
js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt);

JS_END_EXTERN_C

#endif /* jsscan_h___ */

**** End of jsscan.h. ****

**** Start of jsscope.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS symbol tables.
 */
#include "jsstddef.h"
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsscope.h"
#include "jsstr.h"

JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_id(const void *key)
{
    jsval v;
    const JSAtom *atom;

    v = (jsval)key;
    if (JSVAL_IS_INT(v))
	return JSVAL_TO_INT(v);
    atom = key;
    return atom->number;
}

typedef struct JSScopePrivate {
    JSContext *context;
    JSScope *scope;
} JSScopePrivate;

JS_STATIC_DLL_CALLBACK(void *)
js_alloc_scope_space(void *priv, size_t size)
{
    return JS_malloc(((JSScopePrivate *)priv)->context, size);
}

JS_STATIC_DLL_CALLBACK(void)
js_free_scope_space(void *priv, void *item)
{
    JS_free(((JSScopePrivate *)priv)->context, item);
}

JS_STATIC_DLL_CALLBACK(JSHashEntry *)
js_alloc_symbol(void *priv, const void *key)
{
    JSScopePrivate *spriv;
    JSContext *cx;
    JSSymbol *sym;

    spriv = priv;
    JS_ASSERT(JS_IS_SCOPE_LOCKED(spriv->scope));
    cx = spriv->context;
    sym = JS_malloc(cx, sizeof(JSSymbol));
    if (!sym)
	return NULL;
    sym->entry.key = key;
    return &sym->entry;
}

JS_STATIC_DLL_CALLBACK(void)
js_free_symbol(void *priv, JSHashEntry *he, uintN flag)
{
    JSScopePrivate *spriv;
    JSContext *cx;
    JSSymbol *sym, **sp;
    JSScopeProperty *sprop;

    spriv = priv;
    JS_ASSERT(JS_IS_SCOPE_LOCKED(spriv->scope));
    cx = spriv->context;
    sym = (JSSymbol *)he;
    sprop = sym->entry.value;
    if (sprop) {
	sym->entry.value = NULL;
	sprop = js_DropScopeProperty(cx, spriv->scope, sprop);
	if (sprop) {
	    for (sp = &sprop->symbols; *sp; sp = &(*sp)->next) {
		if (*sp == sym) {
		    *sp = sym->next;
		    if (!*sp)
			break;
		}
	    }
	    sym->next = NULL;
	}
    }

    if (flag == HT_FREE_ENTRY)
	JS_free(cx, he);
}

static JSHashAllocOps hash_scope_alloc_ops = {
    js_alloc_scope_space, js_free_scope_space,
    js_alloc_symbol, js_free_symbol
};

/************************************************************************/

JS_STATIC_DLL_CALLBACK(JSSymbol *)
js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsid id, JSHashNumber hash)
{
    JSHashTable *table = scope->data;
    JSHashEntry **hep;
    JSSymbol *sym;

    hep = JS_HashTableRawLookup(table, hash, (const void *)id);
    sym = (JSSymbol *) *hep;
    return sym;
}

#define SCOPE_ADD(PRIV, CLASS_SPECIFIC_CODE)                                  \
    JS_BEGIN_MACRO                                                            \
	if (sym) {                                                            \
	    if (sym->entry.value == sprop)                                    \
		return sym;                                                   \
	    if (sym->entry.value)                                             \
		js_free_symbol(PRIV, &sym->entry, HT_FREE_VALUE);             \
	} else {                                                              \
	    CLASS_SPECIFIC_CODE                                               \
	    sym->scope = scope;                                               \
	    sym->next = NULL;                                                 \
	}                                                                     \
	if (sprop) {                                                          \
	    sym->entry.value = js_HoldScopeProperty(cx, scope, sprop);        \
	    for (sp = &sprop->symbols; *sp; sp = &(*sp)->next)                \
		;                                                             \
	    *sp = sym;                                                        \
	} else {                                                              \
	    sym->entry.value = NULL;                                          \
	}                                                                     \
    JS_END_MACRO

JS_STATIC_DLL_CALLBACK(JSSymbol *)
js_hash_scope_add(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty *sprop)
{
    JSHashTable *table = scope->data;
    const void *key;
    JSHashNumber keyHash;
    JSHashEntry **hep;
    JSSymbol *sym, **sp;
    JSScopePrivate *priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    priv = table->allocPriv;
    priv->context = cx;
    key = (const void *)id;
    keyHash = js_hash_id(key);
    hep = JS_HashTableRawLookup(table, keyHash, key);
    sym = (JSSymbol *) *hep;
    SCOPE_ADD(priv,
	sym = (JSSymbol *) JS_HashTableRawAdd(table, hep, keyHash, key, NULL);
	if (!sym)
	    return NULL;
    );
    return sym;
}

JS_STATIC_DLL_CALLBACK(JSBool)
js_hash_scope_remove(JSContext *cx, JSScope *scope, jsid id)
{
    JSHashTable *table = scope->data;
    JSScopePrivate *priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    priv = table->allocPriv;
    priv->context = cx;
    return JS_HashTableRemove(table, (const void *)id);
}

/* Forward declaration for use by js_hash_scope_clear(). */
extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops;

JS_STATIC_DLL_CALLBACK(void)
js_hash_scope_clear(JSContext *cx, JSScope *scope)
{
    JSHashTable *table = scope->data;
    JSScopePrivate *priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    priv = table->allocPriv;
    priv->context = cx;
    JS_HashTableDestroy(table);
    JS_free(cx, priv);
    scope->ops = &js_list_scope_ops;
    scope->data = NULL;
}

JSScopeOps js_hash_scope_ops = {
    js_hash_scope_lookup,
    js_hash_scope_add,
    js_hash_scope_remove,
    js_hash_scope_clear
};

/************************************************************************/

static void
move_sym_to_front(JSSymbol *sym, JSSymbol **nextp, JSSymbol **front)
{
    *nextp = (JSSymbol*)sym->entry.next;
    sym->entry.next = &(*front)->entry;
    *front = sym;
}

JS_STATIC_DLL_CALLBACK(JSSymbol *)
js_list_scope_lookup(JSContext *cx, JSScope *scope, jsid id, JSHashNumber hash)
{
    JSSymbol *sym, **sp;
    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0;
	 sp = (JSSymbol **)&sym->entry.next) {
	if (sym_id(sym) == id) {
	    /* Move sym to the front for shorter searches. */
            move_sym_to_front(sym,sp,(JSSymbol**)&scope->data);
	    return sym;
	}
    }
    return NULL;
}

#define HASH_THRESHOLD	5

JS_STATIC_DLL_CALLBACK(JSSymbol *)
js_list_scope_add(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty *sprop)
{
    JSSymbol *list = scope->data;
    uint32 nsyms;
    JSSymbol *sym, *next, **sp;
    JSHashTable *table;
    JSHashEntry **hep;
    JSScopePrivate priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    nsyms = 0;
    for (sym = list; sym; sym = (JSSymbol *)sym->entry.next) {
	if (sym_id(sym) == id)
	    break;
	nsyms++;
    }

    if (nsyms >= HASH_THRESHOLD) {
	JSScopePrivate *priv = JS_malloc(cx, sizeof(JSScopePrivate));
	if (!priv) return NULL;
	priv->context = cx;
	priv->scope = scope;
	table = JS_NewHashTable(nsyms, js_hash_id,
				JS_CompareValues, JS_CompareValues,
				&hash_scope_alloc_ops, priv);
	if (table) {
	    for (sym = list; sym; sym = next) {
		/* Save next for loop update, before it changes in lookup. */
		next = (JSSymbol *)sym->entry.next;

		/* Now compute missing keyHash fields. */
		sym->entry.keyHash = js_hash_id(sym->entry.key);
		sym->entry.next = NULL;
		hep = JS_HashTableRawLookup(table,
					    sym->entry.keyHash,
					    sym->entry.key);
		*hep = &sym->entry;
	    }
	    table->nentries = nsyms;
	    scope->ops = &js_hash_scope_ops;
	    scope->data = table;
	    return scope->ops->add(cx, scope, id, sprop);
	}
    }

    priv.context = cx;
    priv.scope = scope;
    SCOPE_ADD(&priv,
	sym = (JSSymbol *)js_alloc_symbol(&priv, (const void *)id);
	if (!sym)
	    return NULL;
	/* Don't set keyHash until we know we need it, above. */
	sym->entry.next = scope->data;
	scope->data = sym;
    );
    return sym;
}

JS_STATIC_DLL_CALLBACK(JSBool)
js_list_scope_remove(JSContext *cx, JSScope *scope, jsid id)
{
    JSSymbol *sym, **sp;
    JSScopePrivate priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0;
	 sp = (JSSymbol **)&sym->entry.next) {
	if (sym_id(sym) == id) {
	    *sp = (JSSymbol *)sym->entry.next;
	    priv.context = cx;
	    priv.scope = scope;
	    js_free_symbol(&priv, &sym->entry, HT_FREE_ENTRY);
	    return JS_TRUE;
	}
    }
    return JS_FALSE;
}

JS_STATIC_DLL_CALLBACK(void)
js_list_scope_clear(JSContext *cx, JSScope *scope)
{
    JSSymbol *sym;
    JSScopePrivate priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    while ((sym = scope->data) != NULL) {
	scope->data = sym->entry.next;
	priv.context = cx;
	priv.scope = scope;
	js_free_symbol(&priv, &sym->entry, HT_FREE_ENTRY);
    }
}

JSScopeOps JS_FRIEND_DATA(js_list_scope_ops) = {
    js_list_scope_lookup,
    js_list_scope_add,
    js_list_scope_remove,
    js_list_scope_clear
};

/************************************************************************/

JSScope *
js_GetMutableScope(JSContext *cx, JSObject *obj)
{
    JSScope *scope, *newscope;

    scope = (JSScope *) obj->map;
    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    if (scope->object == obj)
	return scope;
    newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj),
			   obj);
    if (!newscope)
	return NULL;
    JS_LOCK_SCOPE(cx, newscope);
    obj->map = js_HoldObjectMap(cx, &newscope->map);
    scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj);
    JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
    return newscope;
}

JSScope *
js_MutateScope(JSContext *cx, JSObject *obj, jsid id,
	       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
	       JSScopeProperty **propp)
{
    /* XXX pessimal */
    *propp = NULL;
    return js_GetMutableScope(cx, obj);
}

JSScope *
js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,
	    JSObject *obj)
{
    JSScope *scope;

    scope = JS_malloc(cx, sizeof(JSScope));
    if (!scope)
	return NULL;
    js_InitObjectMap(&scope->map, nrefs, ops, clasp);
    scope->object = obj;
    scope->props = NULL;
    scope->proptail = &scope->props;
    scope->ops = &js_list_scope_ops;
    scope->data = NULL;

#ifdef JS_THREADSAFE
    js_NewLock(&scope->lock);
    scope->count = 0;
#ifdef DEBUG
    scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL;
    scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0;
#endif
#endif

    return scope;
}

void
js_DestroyScope(JSContext *cx, JSScope *scope)
{
    JS_LOCK_SCOPE(cx, scope);
    scope->ops->clear(cx, scope);
    JS_UNLOCK_SCOPE(cx, scope);
#ifdef JS_THREADSAFE
    JS_ASSERT(scope->count == 0);
    js_DestroyLock(&scope->lock);
#endif
    JS_free(cx, scope);
}

JSHashNumber
js_HashValue(jsval v)
{
    return js_hash_id((const void *)v);
}

jsval
js_IdToValue(jsid id)
{
    JSAtom *atom;

    if (JSVAL_IS_INT(id))
	return id;
    atom = (JSAtom *)id;
    return ATOM_KEY(atom);
}

JSScopeProperty *
js_NewScopeProperty(JSContext *cx, JSScope *scope, jsid id,
		    JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
{
    uint32 slot;
    JSScopeProperty *sprop;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    if (!js_AllocSlot(cx, scope->object, &slot))
	return NULL;
    sprop = JS_malloc(cx, sizeof(JSScopeProperty));
    if (!sprop) {
	js_FreeSlot(cx, scope->object, slot);
	return NULL;
    }
    sprop->nrefs = 0;
    sprop->id = js_IdToValue(id);
    sprop->getter = getter;
    sprop->setter = setter;
    sprop->slot = slot;
    sprop->attrs = attrs;
    sprop->spare = 0;
    sprop->symbols = NULL;
    sprop->next = NULL;
    sprop->prevp = scope->proptail;
    *scope->proptail = sprop;
    scope->proptail = &sprop->next;
    return sprop;
}

void
js_DestroyScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop)
{
    /*
     * Test whether obj was finalized before prop's last dereference.
     *
     * This can happen when a deleted property is held by a property iterator
     * object (which points to obj, keeping obj alive so long as the property
     * iterator is reachable).  As soon as the GC finds the iterator to be
     * unreachable, it will finalize the iterator, and may also finalize obj,
     * in an unpredictable order.  If obj is finalized first, its map will be
     * null below, and we need only free prop.
     *
     * In the more typical case of a property whose last reference (from a
     * symbol in obj's scope) goes away before obj is finalized, we must be
     * sure to free prop's slot and unlink it from obj's property list.
     */
    if (scope) {
	JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
	if (scope->object) {
	    js_FreeSlot(cx, scope->object, sprop->slot);
	    *sprop->prevp = sprop->next;
	    if (sprop->next)
		sprop->next->prevp = sprop->prevp;
	    else
		scope->proptail = sprop->prevp;
	}
    }

    /* Purge any cached weak links to prop, then free it. */
    js_FlushPropertyCacheByProp(cx, (JSProperty *)sprop);
    JS_free(cx, sprop);
}

JSScopeProperty *
js_HoldScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop)
{
    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    if (sprop) {
	JS_ASSERT(sprop->nrefs >= 0);
	sprop->nrefs++;
    }
    return sprop;
}

JSScopeProperty *
js_DropScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop)
{
    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));
    if (sprop) {
	JS_ASSERT(sprop->nrefs > 0);
	if (--sprop->nrefs == 0) {
	    js_DestroyScopeProperty(cx, scope, sprop);
	    sprop = NULL;
	}
    }
    return sprop;
}

**** End of jsscope.c. ****

**** Start of jsscope.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsscope_h___
#define jsscope_h___
/*
 * JS symbol tables.
 */
#include "jstypes.h"
#include "jshash.h" /* Added by JSIFY */
#include "jsobj.h"
#include "jsprvtd.h"
#include "jspubtd.h"

struct JSScopeOps {
    JSSymbol *      (*lookup)(JSContext *cx, JSScope *scope, jsid id,
			      JSHashNumber hash);
    JSSymbol *      (*add)(JSContext *cx, JSScope *scope, jsid id,
			   JSScopeProperty *sprop);
    JSBool          (*remove)(JSContext *cx, JSScope *scope, jsid id);
    void            (*clear)(JSContext *cx, JSScope *scope);
};

struct JSScope {
    JSObjectMap     map;                /* base class state */
    JSObject        *object;            /* object that owns this scope */
    JSScopeProperty *props;             /* property list in definition order */
    JSScopeProperty **proptail;         /* pointer to pointer to last prop */
    JSScopeOps      *ops;               /* virtual operations */
    void            *data;              /* private data specific to ops */
#ifdef JS_THREADSAFE
    JSThinLock      lock;              /* binary semaphore protecting scope */
    int32           count;              /* entry count for reentrancy */
#ifdef DEBUG
    const char      *file[4];           /* file where lock was (re-)taken */
    unsigned int    line[4];            /* line where lock was (re-)taken */
#endif
#endif
};

struct JSSymbol {
    JSHashEntry     entry;              /* base class state */
    JSScope         *scope;             /* pointer to owning scope */
    JSSymbol        *next;              /* next in type-specific list */
};

#define sym_id(sym)             ((jsid)(sym)->entry.key)
#define sym_atom(sym)           ((JSAtom *)(sym)->entry.key)
#define sym_property(sym)       ((JSScopeProperty *)(sym)->entry.value)

struct JSScopeProperty {
    jsrefcount      nrefs;              /* number of referencing symbols */
    jsval           id;                 /* id passed to getter and setter */
    JSPropertyOp    getter;             /* getter and setter methods */
    JSPropertyOp    setter;
    uint32          slot;               /* index in obj->slots vector */
    uint8           attrs;              /* attributes, see jsapi.h JSPROP_ */
    uint8           spare;              /* reserved for future use */
    JSSymbol        *symbols;           /* list of aliasing symbols */
    JSScopeProperty *next;              /* doubly-linked list linkage */
    JSScopeProperty **prevp;
};

/*
 * These macros are designed to decouple getter and setter from sprop, by
 * passing obj2 (in whose scope sprop lives, and in whose scope getter and
 * setter might be stored apart from sprop -- say in scope->opTable[i] for
 * a compressed getter or setter index i that is stored in sprop).
 */
#define SPROP_GET(cx,sprop,obj,obj2,vp) ((sprop)->getter(cx,obj,sprop->id,vp))
#define SPROP_SET(cx,sprop,obj,obj2,vp) ((sprop)->setter(cx,obj,sprop->id,vp))

extern JSScope *
js_GetMutableScope(JSContext *cx, JSObject *obj);

extern JSScope *
js_MutateScope(JSContext *cx, JSObject *obj, jsid id,
	       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
	       JSScopeProperty **propp);

extern JSScope *
js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,
	    JSObject *obj);

extern void
js_DestroyScope(JSContext *cx, JSScope *scope);

extern JSHashNumber
js_HashValue(jsval v);

extern jsval
js_IdToValue(jsid id);

extern JSScopeProperty *
js_NewScopeProperty(JSContext *cx, JSScope *scope, jsid id,
		    JSPropertyOp getter, JSPropertyOp setter, uintN attrs);

extern void
js_DestroyScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop);

extern JSScopeProperty *
js_HoldScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop);

extern JSScopeProperty *
js_DropScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop);

#endif /* jsscope_h___ */

**** End of jsscope.h. ****

**** Start of jsscript.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS script operations.
 */
#include "jsstddef.h"
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsinterp.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsscript.h"
#if JS_HAS_XDR
#include "jsxdrapi.h"
#endif

#if JS_HAS_SCRIPT_OBJECT

#if JS_HAS_TOSOURCE
static JSBool
script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    JSScript *script;
    size_t i, j, k, n;
    char buf[16];
    jschar *s, *t;
    uint32 indent;
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
	return JS_FALSE;
    script = JS_GetPrivate(cx, obj);

    /* Let n count the source string length, j the "front porch" length. */
    j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
    n = j + 2;
    if (!script) {
	/* Let k count the constructor argument string length. */
	k = 0;
        s = NULL;               /* quell GCC overwarning */
    } else {
	indent = 0;
	if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
	    return JS_FALSE;
	str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
				 (uintN)indent);
	if (!str)
	    return JS_FALSE;
	str = js_QuoteString(cx, str, '\'');
	if (!str)
	    return JS_FALSE;
	s = str->chars;
	k = str->length;
	n += k;
    }

    /* Allocate the source string and copy into it. */
    t = JS_malloc(cx, (n + 1) * sizeof(jschar));
    if (!t)
	return JS_FALSE;
    for (i = 0; i < j; i++)
	t[i] = buf[i];
    for (j = 0; j < k; i++, j++)
	t[i] = s[j];
    t[i++] = ')';
    t[i++] = ')';
    t[i] = 0;

    /* Create and return a JS string for t. */
    str = JS_NewUCString(cx, t, n);
    if (!str) {
	JS_free(cx, t);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#endif /* JS_HAS_TOSOURCE */

static JSBool
script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    JSScript *script;
    uint32 indent;
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
	return JS_FALSE;
    script = JS_GetPrivate(cx, obj);
    if (!script) {
	*rval = STRING_TO_JSVAL(cx->runtime->emptyString);
	return JS_TRUE;
    }

    indent = 0;
    if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
	return JS_FALSE;
    str = JS_DecompileScript(cx, script, "Script.prototype.toString",
			     (uintN)indent);
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	       jsval *rval)
{
    JSScript *oldscript, *script;
    JSString *str;
    JSStackFrame *fp, *caller;
    JSObject *scopeobj;
    const char *file;
    uintN line;
    JSPrincipals *principals;

    /* If no args, leave private undefined and return early. */
    if (argc == 0)
	goto out;

    /* Otherwise, the first arg is the script source to compile. */
    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;

    /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
    fp = cx->fp;
    caller = fp->down;
    JS_ASSERT(fp->scopeChain == caller->scopeChain);

    scopeobj = NULL;
    if (argc >= 2) {
	if (!js_ValueToObject(cx, argv[1], &scopeobj))
	    return JS_FALSE;
	argv[1] = OBJECT_TO_JSVAL(scopeobj);
    }
    if (!scopeobj)
	scopeobj = caller->scopeChain;

    if (caller->script) {
	file = caller->script->filename;
	line = js_PCToLineNumber(caller->script, caller->pc);
	principals = caller->script->principals;
    } else {
	file = NULL;
	line = 0;
	principals = NULL;
    }

    /* Compile the new script using the caller's scope chain, a la eval(). */
    script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
					     str->chars, str->length,
					     file, line);
    if (!script)
	return JS_FALSE;

    /* Swap script for obj's old script, if any. */
    oldscript = JS_GetPrivate(cx, obj);
    if (!JS_SetPrivate(cx, obj, script)) {
	js_DestroyScript(cx, script);
	return JS_FALSE;
    }
    if (oldscript)
	js_DestroyScript(cx, oldscript);

    script->object = obj;
out:
    /* Return the object. */
    *rval = OBJECT_TO_JSVAL(obj);
    return JS_TRUE;
}

static JSBool
script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSScript *script;
    JSStackFrame *fp, *caller;
    JSObject *scopeobj;

    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
	return JS_FALSE;
    script = JS_GetPrivate(cx, obj);
    if (!script)
	return JS_TRUE;

    scopeobj = NULL;
    if (argc) {
	if (!js_ValueToObject(cx, argv[0], &scopeobj))
	    return JS_FALSE;
	argv[0] = OBJECT_TO_JSVAL(scopeobj);
    }

    /* Emulate eval() by using caller's this, scope chain, and sharp array. */
    fp = cx->fp;
    caller = fp->down;
    if (!scopeobj)
	scopeobj = caller->scopeChain;
    fp->thisp = caller->thisp;
    JS_ASSERT(fp->scopeChain == caller->scopeChain);
    fp->sharpArray = caller->sharpArray;
    return js_Execute(cx, scopeobj, script, NULL, fp, JS_FALSE, rval);
}

#if JS_HAS_XDR
static JSBool
XDRAtom1(JSXDRState *xdr, JSAtomListElement *ale)
{
    uint32 type;
    jsval value;

    if (xdr->mode == JSXDR_ENCODE) {
	type = JSVAL_TAG(ATOM_KEY(ale->atom));
	value = ATOM_KEY(ale->atom);
    }

    if (!JS_XDRUint32(xdr, &ale->index) ||
	!JS_XDRValue(xdr, &value))
	return JS_FALSE;

    if (xdr->mode == JSXDR_DECODE) {
	ale->atom = js_AtomizeValue(xdr->cx, value, 0);
	if (!ale->atom)
	    return JS_FALSE;
    }
    return JS_TRUE;
}

static JSBool
XDRAtomMap(JSXDRState *xdr, JSAtomMap *map)
{
    uint32 length;
    uintN i;

    if (xdr->mode == JSXDR_ENCODE)
	length = map->length;

    if (!JS_XDRUint32(xdr, &length))
	return JS_FALSE;

    if (xdr->mode == JSXDR_DECODE) {
	JSContext *cx;
	void *mark;
	JSAtomList al;
	JSAtomListElement *ale;

	cx = xdr->cx;
	mark = JS_ARENA_MARK(&cx->tempPool);
	ATOM_LIST_INIT(&al);
	for (i = 0; i < length; i++) {
	    JS_ARENA_ALLOCATE(ale, &cx->tempPool, sizeof(*ale));
	    if (!ale ||
		!XDRAtom1(xdr, ale)) {
		if (!ale)
		    JS_ReportOutOfMemory(cx);
		JS_ARENA_RELEASE(&cx->tempPool, mark);
		return JS_FALSE;
	    }
	    ale->next = al.list;
	    al.count++;
	    al.list = ale;
	}
	if (!js_InitAtomMap(cx, map, &al)) {
	    JS_ARENA_RELEASE(&cx->tempPool, mark);
	    return JS_FALSE;
	}
	JS_ARENA_RELEASE(&cx->tempPool, mark);
    } else if (xdr->mode == JSXDR_ENCODE) {
	JSAtomListElement ale;

	for (i = 0; i < map->length; i++) {
	    ale.atom = map->vector[i];
	    ale.index = i;
	    if (!XDRAtom1(xdr, &ale))
		return JS_FALSE;
	}
    }
    return JS_TRUE;
}

JSBool
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic)
{
    JSScript *script = *scriptp;
    uint32 length, lineno, depth, magicval;

    if (xdr->mode == JSXDR_ENCODE)
	magicval = SCRIPT_XDRMAGIC;
    if (!JS_XDRUint32(xdr, &magicval))
	return JS_FALSE;
    if (magicval != SCRIPT_XDRMAGIC) {
	*magic = JS_FALSE;
	return JS_TRUE;
    }
    *magic = JS_TRUE;
    if (xdr->mode == JSXDR_ENCODE) {
	length = script->length;
	lineno = (uint32)script->lineno;
	depth = (uint32)script->depth;
    }
    if (!JS_XDRUint32(xdr, &length))
	return JS_FALSE;
    if (xdr->mode == JSXDR_DECODE) {
	script = js_NewScript(xdr->cx, length);
	if (!script)
	    return JS_FALSE;
	*scriptp = script;
    }
    if (!JS_XDRBytes(xdr, (char **)&script->code, length) ||
	!XDRAtomMap(xdr, &script->atomMap) ||
	!JS_XDRCStringOrNull(xdr, (char **)&script->notes) ||
	!JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
	!JS_XDRUint32(xdr, &lineno) ||
	!JS_XDRUint32(xdr, &depth)) {
	if (xdr->mode == JSXDR_DECODE) {
	    js_DestroyScript(xdr->cx, script);
	    *scriptp = NULL;
	}
	return JS_FALSE;
    }
    if (xdr->mode == JSXDR_DECODE) {
	script->lineno = (uintN)lineno;
	script->depth = (uintN)depth;
    }
    return JS_TRUE;
}

static JSBool
script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    JSXDRState *xdr;
    JSScript *script;
    JSBool ok, magic;
    uint32 len;
    void *buf;
    JSString *str;

    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
	return JS_FALSE;
    script = JS_GetPrivate(cx, obj);
    if (!script)
	return JS_TRUE;

    /* create new XDR */
    xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
    if (!xdr)
	return JS_FALSE;

    /* write  */
    ok = js_XDRScript(xdr, &script, &magic);
    if (!ok)
	goto out;
    if (!magic) {
	*rval = JSVAL_VOID;
	goto out;
    }

    buf = JS_XDRMemGetData(xdr, &len);
    if (!buf) {
	ok = JS_FALSE;
	goto out;
    }

    JS_ASSERT((jsword)buf % sizeof(jschar) == 0);
    len /= sizeof(jschar);
    str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);
    if (!str) {
	ok = JS_FALSE;
	goto out;
    }

#if IS_BIG_ENDIAN
  {
    jschar *chars;
    uint32 i;

    /* Swap bytes in Unichars to keep frozen strings machine-independent. */
    chars = JS_GetStringChars(str);
    for (i = 0; i < len; i++)
	chars[i] = JSXDR_SWAB16(chars[i]);
  }
#endif
    *rval = STRING_TO_JSVAL(str);

out:
    JS_XDRDestroy(xdr);
    return ok;
}

static JSBool
script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	    jsval *rval)
{
    JSXDRState *xdr;
    JSString *str;
    void *buf;
    uint32 len;
    JSScript *script, *oldscript;
    JSBool ok, magic;

    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
	return JS_FALSE;

    if (argc == 0)
	return JS_TRUE;
    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;

    /* create new XDR */
    xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
    if (!xdr)
	return JS_FALSE;

    buf = JS_GetStringChars(str);
    len = JS_GetStringLength(str);
#if IS_BIG_ENDIAN
  {
    jschar *from, *to;
    uint32 i;

    /* Swap bytes in Unichars to keep frozen strings machine-independent. */
    from = (jschar *)buf;
    to = JS_malloc(cx, len * sizeof(jschar));
    if (!to)
	return JS_FALSE;
    for (i = 0; i < len; i++)
	to[i] = JSXDR_SWAB16(from[i]);
    buf = (char *)to;
  }
#endif
    len *= sizeof(jschar);
    JS_XDRMemSetData(xdr, buf, len);

    /* XXXbe should magic mismatch be error, or false return value? */
    ok = js_XDRScript(xdr, &script, &magic);
    if (!ok)
	goto out;
    if (!magic) {
	*rval = JSVAL_FALSE;
	goto out;
    }

    /* Swap script for obj's old script, if any. */
    oldscript = JS_GetPrivate(cx, obj);
    ok = JS_SetPrivate(cx, obj, script);
    if (!ok) {
	JS_free(cx, script);
	goto out;
    }
    if (oldscript)
	js_DestroyScript(cx, oldscript);

    script->object = obj;

out:
    /*
     * We reset the buffer to be NULL so that it doesn't free the chars
     * memory owned by str (argv[0]).
     */
    JS_XDRMemSetData(xdr, NULL, 0);
    JS_XDRDestroy(xdr);
#if IS_BIG_ENDIAN
    JS_free(cx, buf);
#endif
    *rval = JSVAL_TRUE;
    return ok;
}

#endif /* JS_HAS_XDR */

static char js_thaw_str[] = "thaw";

static JSFunctionSpec script_methods[] = {
#if JS_HAS_TOSOURCE
    {js_toSource_str,   script_toSource,        0},
#endif
    {js_toString_str,   script_toString,        0},
    {"compile",         script_compile,         2},
    {"exec",            script_exec,            1},
#if JS_HAS_XDR
    {"freeze",		script_freeze,		0},
    {js_thaw_str,	script_thaw,		1},
#endif /* JS_HAS_XDR */
    {0}
};

#endif /* JS_HAS_SCRIPT_OBJECT */

static void
script_finalize(JSContext *cx, JSObject *obj)
{
    JSScript *script;

    script = JS_GetPrivate(cx, obj);
    if (script)
	js_DestroyScript(cx, script);
}

static JSBool
script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
}

JSClass js_ScriptClass = {
    "Script",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   script_finalize,
    NULL,             NULL,             script_call,      NULL,/*XXXbe xdr*/
};

#if JS_HAS_SCRIPT_OBJECT

static JSBool
Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    /* If not constructing, replace obj with a new Script object. */
    if (!cx->fp->constructing) {
	obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
	if (!obj)
	    return JS_FALSE;
    }
    return script_compile(cx, obj, argc, argv, rval);
}

#if JS_HAS_XDR

static JSBool
script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		   jsval *rval)
{
    obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
    if (!obj)
	return JS_FALSE;
    if (!script_thaw(cx, obj, argc, argv, rval))
	return JS_FALSE;
    *rval = OBJECT_TO_JSVAL(obj);
    return JS_TRUE;
}

static JSFunctionSpec script_static_methods[] = {
    {js_thaw_str,       script_static_thaw,     1},
    {0}
};

#else  /* !JS_HAS_XDR */

#define script_static_methods   NULL

#endif /* !JS_HAS_XDR */

JSObject *
js_InitScriptClass(JSContext *cx, JSObject *obj)
{
    return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
			NULL, script_methods, NULL, script_static_methods);
}

#endif /* JS_HAS_SCRIPT_OBJECT */

JSScript *
js_NewScript(JSContext *cx, uint32 length)
{
    JSScript *script;

    script = JS_malloc(cx, sizeof(JSScript) + length * sizeof(jsbytecode));
    if (!script)
	return NULL;
    memset(script, 0, sizeof(JSScript));
    script->code = (jsbytecode *)(script + 1);
    script->length = length;
    return script;
}

JSScript *
js_NewScriptFromParams(JSContext *cx, jsbytecode *code, uint32 length,
		       const char *filename, uintN lineno, uintN depth,
		       jssrcnote *notes, JSTryNote *trynotes,
		       JSPrincipals *principals)
{
    JSScript *script;

    script = js_NewScript(cx, length);
    if (!script)
	return NULL;
    memcpy(script->code, code, length * sizeof(jsbytecode));
    if (filename) {
	script->filename = JS_strdup(cx, filename);
	if (!script->filename) {
	    js_DestroyScript(cx, script);
	    return NULL;
	}
    }
    script->lineno = lineno;
    script->depth = depth;
    script->notes = notes;
    script->trynotes = trynotes;
    if (principals)
	JSPRINCIPALS_HOLD(cx, principals);
    script->principals = principals;
    return script;
}

JS_FRIEND_API(JSScript *)
js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
{
    JSTryNote *trynotes;
    jssrcnote *notes;
    JSScript *script;
    JSRuntime *rt;
    JSNewScriptHook hook;

    if (!js_FinishTakingTryNotes(cx, cg, &trynotes))
	return NULL;
    notes = js_FinishTakingSrcNotes(cx, cg);
    script = js_NewScriptFromParams(cx, cg->base, CG_OFFSET(cg),
				    cg->filename, cg->firstLine,
				    cg->maxStackDepth, notes, trynotes,
				    cg->principals);
    if (!script)
	return NULL;
    if (!notes || !js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) {
	js_DestroyScript(cx, script);
	return NULL;
    }

    /* Tell the debugger about this compiled script. */
    rt = cx->runtime;
    hook = rt->newScriptHook;
    if (hook) {
	(*hook)(cx, cg->filename, cg->firstLine, script, fun,
		rt->newScriptHookData);
    }
    return script;
}

void
js_DestroyScript(JSContext *cx, JSScript *script)
{
    JSRuntime *rt;
    JSDestroyScriptHook hook;

    rt = cx->runtime;
    hook = rt->destroyScriptHook;
    if (hook)
	(*hook)(cx, script, rt->destroyScriptHookData);

    JS_ClearScriptTraps(cx, script);
    js_FreeAtomMap(cx, &script->atomMap);
    JS_free(cx, (void *)script->filename);
    JS_free(cx, script->notes);
    JS_free(cx, script->trynotes);
    if (script->principals)
	JSPRINCIPALS_DROP(cx, script->principals);
    JS_free(cx, script);
}

jssrcnote *
js_GetSrcNote(JSScript *script, jsbytecode *pc)
{
    jssrcnote *sn;
    ptrdiff_t offset, target;

    sn = script->notes;
    if (!sn)
	return NULL;
    target = PTRDIFF(pc, script->code, jsbytecode);
    if ((uintN)target >= script->length)
	return NULL;
    for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
	offset += SN_DELTA(sn);
	if (offset == target && SN_IS_GETTABLE(sn))
	    return sn;
    }
    return NULL;
}

uintN
js_PCToLineNumber(JSScript *script, jsbytecode *pc)
{
    jssrcnote *sn;
    ptrdiff_t offset, target;
    uintN lineno;
    JSSrcNoteType type;

    sn = script->notes;
    if (!sn)
	return 0;
    target = PTRDIFF(pc, script->code, jsbytecode);
    lineno = script->lineno;
    for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
	offset += SN_DELTA(sn);
	type = SN_TYPE(sn);
	if (type == SRC_SETLINE) {
	    if (offset <= target)
		lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
	} else if (type == SRC_NEWLINE) {
	    if (offset <= target)
		lineno++;
	}
	if (offset > target)
	    break;
    }
    return lineno;
}

jsbytecode *
js_LineNumberToPC(JSScript *script, uintN target)
{
    jssrcnote *sn;
    uintN lineno;
    ptrdiff_t offset;
    JSSrcNoteType type;

    sn = script->notes;
    if (!sn)
	return NULL;
    lineno = script->lineno;
    for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
	if (lineno >= target)
	    break;
	offset += SN_DELTA(sn);
	type = SN_TYPE(sn);
	if (type == SRC_SETLINE) {
	    lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
	} else if (type == SRC_NEWLINE) {
	    lineno++;
	}
    }
    return script->code + offset;
}

uintN
js_GetScriptLineExtent(JSScript *script)
{
    jssrcnote *sn;
    uintN lineno;
    JSSrcNoteType type;

    sn = script->notes;
    if (!sn)
	return 0;
    lineno = script->lineno;
    for (; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
	type = SN_TYPE(sn);
	if (type == SRC_SETLINE) {
	    lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
	} else if (type == SRC_NEWLINE) {
	    lineno++;
	}
    }
    return 1 + lineno - script->lineno;
}

**** End of jsscript.c. ****

**** Start of jsscript.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsscript_h___
#define jsscript_h___
/*
 * JS script descriptor.
 */
#include "jsatom.h"
#include "jsprvtd.h"

JS_BEGIN_EXTERN_C

/*
 * Exception handling runtime information.
 *
 * All fields except length are code offsets, relative to the beginning of
 * the script.  If script->trynotes is not null, it points to a vector of
 * these structs terminated by one with start, catchStart, and finallyStart
 * all equal to 0, and length == script->length.
 */
struct JSTryNote {
    ptrdiff_t    start;         /* start of try statement */
    ptrdiff_t    length;        /* count of try statement bytecodes */
    ptrdiff_t    catchStart;    /* start of catch block (0 if none) */
};

struct JSScript {
    jsbytecode   *code;         /* bytecodes and their immediate operands */
    uint32       length;        /* length of code vector */
    JSAtomMap    atomMap;       /* maps immediate index to literal struct */
    const char   *filename;     /* source filename or null */
    uintN        lineno;        /* base line number of script */
    uintN        depth;         /* maximum stack depth in slots */
    jssrcnote    *notes;        /* line number and other decompiling data */
    JSTryNote    *trynotes;     /* exception table for this script */
    JSPrincipals *principals;   /* principals for this script */
    JSObject     *object;       /* optional Script-class object wrapper */
};

extern JSClass js_ScriptClass;

extern JSObject *
js_InitScriptClass(JSContext *cx, JSObject *obj);

extern JSScript *
js_NewScript(JSContext *cx, uint32 length);

extern JSScript *
js_NewScriptFromParams(JSContext *cx, jsbytecode *code, uint32 length,
		       const char *filename, uintN lineno, uintN depth,
		       jssrcnote *notes, JSTryNote *trynotes,
		       JSPrincipals *principals);

extern JS_FRIEND_API(JSScript *)
js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun);

extern void
js_DestroyScript(JSContext *cx, JSScript *script);

extern jssrcnote *
js_GetSrcNote(JSScript *script, jsbytecode *pc);

extern uintN
js_PCToLineNumber(JSScript *script, jsbytecode *pc);

extern jsbytecode *
js_LineNumberToPC(JSScript *script, uintN lineno);

extern uintN
js_GetScriptLineExtent(JSScript *script);

extern JSBool
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic);

JS_END_EXTERN_C

#endif /* jsscript_h___ */

**** End of jsscript.h. ****

**** Start of jsstddef.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1998
 * Netscape Communications Corporation.  All Rights Reserved.
 */

/*
 * stddef inclusion here to first declare ptrdif as a signed long instead of a
 * signed int.
 */

#ifdef _WINDOWS
# ifndef XP_WIN
# define XP_WIN
# endif
#if defined(_WIN32) || defined(WIN32)
# ifndef XP_WIN32
# define XP_WIN32
# endif
#else
# ifndef XP_WIN16
# define XP_WIN16
# endif
#endif
#endif

#ifdef XP_WIN16
#ifndef _PTRDIFF_T_DEFINED
typedef long ptrdiff_t;

/*
 * The Win16 compiler treats pointer differences as 16-bit signed values.
 * This macro allows us to treat them as 17-bit signed values, stored in
 * a 32-bit type.
 */
#define PTRDIFF(p1, p2, type)                                 \
    ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type))

#define _PTRDIFF_T_DEFINED
#endif /*_PTRDIFF_T_DEFINED*/
#else /*WIN16*/

#define PTRDIFF(p1, p2, type)                                 \
	((p1) - (p2))

#endif

#include <stddef.h>



**** End of jsstddef.h. ****

**** Start of jsstr.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * JS string type implementation.
 *
 * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these
 * native methods store strings (possibly newborn) converted from their 'this'
 * parameter and arguments on the stack: 'this' conversions at argv[-1], arg
 * conversions at their index (argv[0], argv[1]).  This is a legitimate method
 * of rooting things that might lose their newborn root due to subsequent GC
 * allocations in the same native method.
 */
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jshash.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsgc.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsregexp.h"
#include "jsstr.h"

#if JS_HAS_REPLACE_LAMBDA
#include "jsinterp.h"
#endif

/* Contributions from the String class to the set of methods defined for the
 * global object.  escape and unescape used to be defined in the Mocha library,
 * but as ECMA decided to spec them, they've been moved to the core engine
 * and made ECMA-compliant.  (Incomplete escapes are interpreted as literal
 * characters by unescape.)
 */

/* Stuff to emulate the old libmocha escape, which took a second argument
 * giving the type of escape to perform.  Retained for compatibility, and
 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
 */

#define URL_XALPHAS     (unsigned char) 1
#define URL_XPALPHAS    (unsigned char) 2
#define URL_PATH        (unsigned char) 4

static const unsigned char netCharType[256] =
/*	Bit 0		xalpha		-- the alphas
**	Bit 1		xpalpha		-- as xalpha but
**                             converts spaces to plus and plus to %20
**	Bit 2 ...	path		-- as xalphas but doesn't escape '/'
*/
    /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
    {    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	/* 0x */
	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,	/* 1x */
	 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4,	/* 2x   !"#$%&'()*+,-./	 */
	 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,	/* 3x  0123456789:;<=>?	 */
	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,	/* 4x  @ABCDEFGHIJKLMNO  */
	 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,	/* 5X  PQRSTUVWXYZ[\]^_	 */
	 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,	/* 6x  `abcdefghijklmno	 */
	 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,	/* 7X  pqrstuvwxyz{\}~	DEL */
	 0, };

/* This matches the ECMA escape set when mask is 7 (default.) */

#define IS_OK(C, mask) (netCharType[((unsigned char) (C))] & (mask))

/* See ECMA-262 15.1.2.4. */
static JSBool
str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    size_t i, ni, newlength;
    const jschar *chars;
    jschar *newchars;
    jschar ch;
    jsint mask;
    jsdouble d;
    const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
			   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
    if (argc > 1) {
	if (!js_ValueToNumber(cx, argv[1], &d))
	    return JS_FALSE;
	if (!JSDOUBLE_IS_FINITE(d) ||
	    (mask = (jsint)d) != d ||
	    mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
	{
	    char numBuf[12];
	    JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
				 JSMSG_BAD_STRING_MASK, numBuf);
	    return JS_FALSE;
	}
    }

    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;
    argv[0] = STRING_TO_JSVAL(str);

    chars = str->chars;
    newlength = str->length;
    /* Take a first pass and see how big the result string will need to be. */
    for (i = 0; i < str->length; i++) {
	if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
	    continue;
	} else if (ch < 256) {
	    if (mask == URL_XPALPHAS && ch == ' ')
		continue;   /* The character will be encoded as '+' */
	    else
		newlength += 2; /* The character will be encoded as %XX */
	} else {
	    newlength += 5; /* The character will be encoded as %uXXXX */
	}
    }

    newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));
    for (i = 0, ni = 0; i < str->length; i++) {
	if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
	    newchars[ni++] = ch;
	} else if (ch < 256) {
	    if (mask == URL_XPALPHAS && ch == ' ') {
		newchars[ni++] = '+'; /* convert spaces to pluses */
	    } else {
		newchars[ni++] = '%';
		newchars[ni++] = digits[ch >> 4];
		newchars[ni++] = digits[ch & 0xF];
	    }
	} else {
	    newchars[ni++] = '%';
	    newchars[ni++] = 'u';
	    newchars[ni++] = digits[ch >> 12];
	    newchars[ni++] = digits[(ch & 0xF00) >> 8];
	    newchars[ni++] = digits[(ch & 0xF0) >> 4];
	    newchars[ni++] = digits[ch & 0xF];
	}
    }
    JS_ASSERT(ni == newlength);
    newchars[newlength] = 0;

    str = js_NewString(cx, newchars, newlength, 0);
    if (!str) {
	JS_free(cx, newchars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#undef IS_OK

/* See ECMA-262 15.1.2.5 */
static JSBool
str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    size_t i, ni;
    const jschar *chars;
    jschar *newchars;
    jschar ch;

    str = js_ValueToString(cx, argv[0]);
    if (!str)
	return JS_FALSE;
    argv[0] = STRING_TO_JSVAL(str);

    chars = str->chars;
    /* Don't bother allocating less space for the new string. */
    newchars = (jschar *) JS_malloc(cx, (str->length + 1) * sizeof(jschar));
    ni = i = 0;
    while (i < str->length) {
	ch = chars[i++];
	if (ch == '%') {
	    if (i + 1 < str->length &&
		JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
	    {
		ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
		i += 2;
	    } else if (i + 4 < str->length && chars[i] == 'u' &&
		       JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
		       JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
	    {
		ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
			+ JS7_UNHEX(chars[i + 2])) << 4)
		      + JS7_UNHEX(chars[i + 3])) << 4)
		    + JS7_UNHEX(chars[i + 4]);
		i += 5;
	    }
	}
	newchars[ni++] = ch;
    }
    newchars[ni] = 0;

    str = js_NewString(cx, newchars, ni, 0);
    if (!str) {
	JS_free(cx, newchars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSFunctionSpec string_functions[] = {
    {"escape",          str_escape,             1},
    {"unescape",        str_unescape,           1},
    {0}
};

jschar      js_empty_ucstr[]  = {0};
JSSubString js_EmptySubString = {0, js_empty_ucstr};

enum string_tinyid {
    STRING_LENGTH = -1
};

static JSPropertySpec string_props[] = {
    {js_length_str,     STRING_LENGTH,  JSPROP_READONLY},
    {0}
};

static JSBool
str_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    /* Make delete s.length fail even though length is in s.__proto__. */
    if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom))
	*vp = JSVAL_FALSE;
    return JS_TRUE;
}

static JSBool
str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSString *str;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    if (JSVAL_TO_INT(id) == STRING_LENGTH)
	*vp = INT_TO_JSVAL((jsint)str->length);
    return JS_TRUE;
}

static JSBool
str_resolve1(JSContext *cx, JSObject *obj, JSString *str, jsint slot)
{
    jschar buf[2];
    JSString *str1;

    buf[0] = str->chars[slot];
    buf[1] = 0;
    str1 = js_NewStringCopyN(cx, buf, 1, 0);
    if (!str1)
	return JS_FALSE;
    return JS_DefineElement(cx, obj, slot, STRING_TO_JSVAL(str1),
			    JS_PropertyStub, JS_PropertyStub,
			    JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
}

static JSBool
str_enumerate(JSContext *cx, JSObject *obj)
{
    JSString *str;
    JSBool ok;
    jsint i;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    ok = JS_TRUE;
    js_LockGCThing(cx, str);
    for (i = 0; i < (jsint)str->length; i++) {
	ok = str_resolve1(cx, obj, str, i);
	if (!ok)
	    break;
    }
    js_UnlockGCThing(cx, str);
    return ok;
}

static JSBool
str_resolve(JSContext *cx, JSObject *obj, jsval id)
{
    JSString *str;
    jsint slot;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;
    slot = JSVAL_TO_INT(id);
    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    if ((size_t)slot >= str->length)
	return JS_TRUE;
    return str_resolve1(cx, obj, str, slot);
}

static JSClass string_class = {
    "String",
    JSCLASS_HAS_PRIVATE,
    JS_PropertyStub,  str_delProperty,  str_getProperty,  JS_PropertyStub,
    str_enumerate,    str_resolve,      JS_ConvertStub,   JS_FinalizeStub
};

#if JS_HAS_TOSOURCE

/*
 * String.prototype.quote is generic (as are most string methods), unlike
 * toSource, toString, and valueOf.
 */
static JSBool
str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    str = js_QuoteString(cx, str, '"');
    if (!str)
	return JS_FALSE;
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval v;
    JSString *str;
    size_t i, j, k, n;
    char buf[16];
    jschar *s, *t;

    if (!JS_InstanceOf(cx, obj, &string_class, argv))
	return JS_FALSE;
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_STRING(v))
	return js_obj_toSource(cx, obj, argc, argv, rval);
    str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"');
    if (!str)
	return JS_FALSE;
    j = JS_snprintf(buf, sizeof buf, "(new %s(", string_class.name);
    s = str->chars;
    k = str->length;
    n = j + k + 2;
    t = JS_malloc(cx, (n + 1) * sizeof(jschar));
    if (!t)
	return JS_FALSE;
    for (i = 0; i < j; i++)
	t[i] = buf[i];
    for (j = 0; j < k; i++, j++)
	t[i] = s[j];
    t[i++] = ')';
    t[i++] = ')';
    t[i] = 0;
    str = js_NewString(cx, t, n, 0);
    if (!str) {
	JS_free(cx, t);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

#endif /* JS_HAS_TOSOURCE */

static JSBool
str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsval v;

    if (!JS_InstanceOf(cx, obj, &string_class, argv))
	return JS_FALSE;
    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    if (!JSVAL_IS_STRING(v))
	return js_obj_toString(cx, obj, argc, argv, rval);
    *rval = v;
    return JS_TRUE;
}

static JSBool
str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    if (!JS_InstanceOf(cx, obj, &string_class, argv))
	return JS_FALSE;
    *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
    return JS_TRUE;
}

/*
 * Java-like string native methods.
 */
static JSBool
str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    JSString *str;
    jsdouble d;
    jsdouble length, begin, end;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    if (argc != 0) {
	if (!js_ValueToNumber(cx, argv[0], &d))
	    return JS_FALSE;
	length = str->length;
	begin = js_DoubleToInteger(d);
	if (begin < 0)
	    begin = 0;
	else if (begin > length)
	    begin = length;

	if (argc == 1) {
	    end = length;
	} else {
	    if (!js_ValueToNumber(cx, argv[1], &d))
		return JS_FALSE;
	    end = js_DoubleToInteger(d);
	    if (end < 0)
		end = 0;
	    else if (end > length)
		end = length;
	    if (end < begin) {
		if (cx->version != JSVERSION_1_2) {
		    /* XXX emulate old JDK1.0 java.lang.String.substring. */
		    jsdouble tmp = begin;
		    begin = end;
		    end = tmp;
		} else {
		    end = begin;
		}
	    }
	}

	str = js_NewStringCopyN(cx, str->chars + (size_t)begin,
				(size_t)(end - begin), 0);
	if (!str)
	    return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    JSString *str;
    size_t i, n;
    jschar *s, *news;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    n = str->length;
    news = JS_malloc(cx, (n + 1) * sizeof(jschar));
    if (!news)
	return JS_FALSE;
    s = str->chars;
    for (i = 0; i < n; i++)
	news[i] = JS_TOLOWER(s[i]);
    news[n] = 0;
    str = js_NewString(cx, news, n, 0);
    if (!str) {
	JS_free(cx, news);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		jsval *rval)
{
    JSString *str;
    size_t i, n;
    jschar *s, *news;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    n = str->length;
    news = JS_malloc(cx, (n + 1) * sizeof(jschar));
    if (!news)
	return JS_FALSE;
    s = str->chars;
    for (i = 0; i < n; i++)
	news[i] = JS_TOUPPER(s[i]);
    news[n] = 0;
    str = js_NewString(cx, news, n, 0);
    if (!str) {
	JS_free(cx, news);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    jsdouble d;
    size_t index;
    jschar buf[2];

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    if (!js_ValueToNumber(cx, argv[0], &d))
	return JS_FALSE;
    d = js_DoubleToInteger(d);
    if (d < 0 || str->length <= d) {
	*rval = JS_GetEmptyStringValue(cx);
    } else {
	index = (size_t)d;
	buf[0] = str->chars[index];
	buf[1] = 0;
	str = js_NewStringCopyN(cx, buf, 1, 0);
	if (!str)
	    return JS_FALSE;
	*rval = STRING_TO_JSVAL(str);
    }
    return JS_TRUE;
}

static JSBool
str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	       jsval *rval)
{
    JSString *str;
    jsdouble d;
    size_t index;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    if (!js_ValueToNumber(cx, argv[0], &d))
	return JS_FALSE;
    d = js_DoubleToInteger(d);
    if (d < 0 || str->length <= d) {
	*rval = JS_GetNaNValue(cx);
    } else {
	index = (size_t)d;
	*rval = INT_TO_JSVAL((jsint)str->chars[index]);
    }
    return JS_TRUE;
}

jsint
js_BoyerMooreHorspool(const jschar *text, jsint textlen,
		      const jschar *pat, jsint patlen,
		      jsint start)
{
    jsint i, j, k, m;
    uint8 skip[BMH_CHARSET_SIZE];
    jschar c;

    JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX);
    for (i = 0; i < BMH_CHARSET_SIZE; i++)
	skip[i] = (uint8)patlen;
    m = patlen - 1;
    for (i = 0; i < m; i++) {
	c = pat[i];
	if (c >= BMH_CHARSET_SIZE)
	    return BMH_BAD_PATTERN;
	skip[c] = (uint8)(m - i);
    }
    for (k = start + m;
	 k < textlen;
	 k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) {
	for (i = k, j = m; ; i--, j--) {
	    if (j < 0)
		return i + 1;
	    if (text[i] != pat[j])
		break;
	}
    }
    return -1;
}

static JSBool
str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str, *str2;
    jsint i, j, index, textlen, patlen;
    const jschar *text, *pat;
    jsdouble d;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);
    text = str->chars;
    textlen = (jsint)str->length;

    str2 = js_ValueToString(cx, argv[0]);
    if (!str2)
	return JS_FALSE;
    argv[0] = STRING_TO_JSVAL(str2);
    pat = str2->chars;
    patlen = (jsint)str2->length;

    if (argc > 1) {
	if (!js_ValueToNumber(cx, argv[1], &d))
	    return JS_FALSE;
	d = js_DoubleToInteger(d);
	if (d < 0)
	    i = 0;
	else if (d > textlen)
	    i = textlen;
	else
	    i = (jsint)d;
    } else {
	i = 0;
    }
    if (patlen == 0) {
	*rval = INT_TO_JSVAL(i);
	return JS_TRUE;
    }

    /* XXX tune the BMH threshold (512) */
    if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) {
	index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i);
	if (index != BMH_BAD_PATTERN)
	    goto out;
    }

    index = -1;
    j = 0;
    while (i + j < textlen) {
	if (text[i + j] == pat[j]) {
	    if (++j == patlen) {
		index = i;
		break;
	    }
	} else {
	    i++;
	    j = 0;
	}
    }

out:
    *rval = INT_TO_JSVAL(index);
    return JS_TRUE;
}

static JSBool
str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		  jsval *rval)
{
    JSString *str, *str2;
    const jschar *text, *pat;
    jsint i, j, textlen, patlen;
    jsdouble d;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);
    text = str->chars;
    textlen = (jsint)str->length;

    str2 = js_ValueToString(cx, argv[0]);
    if (!str2)
	return JS_FALSE;
    argv[0] = STRING_TO_JSVAL(str2);
    pat = str2->chars;
    patlen = (jsint)str2->length;

    if (argc > 1) {
	if (!js_ValueToNumber(cx, argv[1], &d))
	    return JS_FALSE;
	if (JSDOUBLE_IS_NaN(d)) {
	    i = textlen;
	} else {
	    d = js_DoubleToInteger(d);
	    if (d < 0)
		i = 0;
	    else if (d > textlen - patlen)
		i = textlen - patlen;
	    else
		i = (jsint)d;
	}
    } else {
	i = textlen;
    }

    if (patlen == 0) {
	*rval = INT_TO_JSVAL(i);
	return JS_TRUE;
    }

    j = 0;
    while (i >= 0) {
	if (text[i + j] == pat[j]) {
	    if (++j == patlen)
		break;
	} else {
	    i--;
	    j = 0;
	}
    }
    *rval = INT_TO_JSVAL(i);
    return JS_TRUE;
}

/*
 * Perl-inspired string functions.
 */
#if !JS_HAS_MORE_PERL_FUN || !JS_HAS_REGEXPS
static JSBool
str_nyi(JSContext *cx, const char *what)
{
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
			 JSMSG_NO_STRING_PROTO, what);
    return JS_FALSE;
}
#endif

#if JS_HAS_REGEXPS
typedef enum GlobMode {
    GLOB_MATCH,
    GLOB_REPLACE,
    GLOB_SEARCH
} GlobMode;

typedef struct GlobData {
    uintN    optarg;    /* input: index of optional flags argument */
    GlobMode mode;      /* input: return index, match object, or void */
    JSBool   global;    /* output: whether regexp was global */
    JSString *str;      /* output: 'this' parameter object as string */
    JSRegExp *regexp;   /* output: regexp parameter object private data */
} GlobData;

static JSBool
match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		 JSBool (*glob)(JSContext *cx, jsint count, GlobData *data),
		 GlobData *data, jsval *rval)
{
    JSString *str, *src, *opt;
    JSObject *reobj;
    JSRegExp *re;
    size_t index;
    JSBool ok;
    jsint count;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);
    data->str = str;

    if (JSVAL_IS_REGEXP(cx, argv[0])) {
	reobj = JSVAL_TO_OBJECT(argv[0]);
	re = JS_GetPrivate(cx, reobj);
    } else {
	src = js_ValueToString(cx, argv[0]);
	if (!src)
	    return JS_FALSE;
	if (data->optarg < argc) {
	    argv[0] = STRING_TO_JSVAL(src);
	    opt = js_ValueToString(cx, argv[data->optarg]);
	    if (!opt)
		return JS_FALSE;
	} else {
	    opt = NULL;
	}
	re = js_NewRegExpOpt(cx, src, opt);
	if (!re)
	    return JS_FALSE;
	reobj = NULL;
    }
    data->regexp = re;

    if (reobj)
	JS_LOCK_OBJ(cx, reobj);

    data->global = (re->flags & JSREG_GLOB) != 0;
    index = 0;
    if (data->mode == GLOB_SEARCH) {
	ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval);
	if (ok) {
	    *rval = (*rval == JSVAL_TRUE)
		    ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length)
		    : INT_TO_JSVAL(-1);
	}
    } else if (data->global) {
	ok = JS_TRUE;
	re->lastIndex = 0;
	for (count = 0; index <= str->length; count++) {
	    ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval);
	    if (!ok || *rval != JSVAL_TRUE)
		break;
	    ok = glob(cx, count, data);
	    if (!ok)
		break;
	    if (cx->regExpStatics.lastMatch.length == 0) {
		if (index == str->length)
		    break;
		index++;
	    }
	}
    } else {
	ok = js_ExecuteRegExp(cx, re, str, &index, data->mode == GLOB_REPLACE,
			      rval);
    }

    if (reobj) {
	JS_UNLOCK_OBJ(cx, reobj);
    } else {
	js_DestroyRegExp(cx, re);
    }
    return ok;
}

typedef struct MatchData {
    GlobData base;
    JSObject *arrayobj;
} MatchData;

static JSBool
match_glob(JSContext *cx, jsint count, GlobData *data)
{
    MatchData *mdata;
    JSObject *arrayobj;
    JSSubString *matchsub;
    JSString *matchstr;
    jsval v;

    mdata = (MatchData *)data;
    arrayobj = mdata->arrayobj;
    if (!arrayobj) {
	arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL);
	if (!arrayobj)
	    return JS_FALSE;
	mdata->arrayobj = arrayobj;
    }
    matchsub = &cx->regExpStatics.lastMatch;
    matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0);
    if (!matchstr)
	return JS_FALSE;
    v = STRING_TO_JSVAL(matchstr);
    return js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v);
}
#endif /* JS_HAS_REGEXPS */

static JSBool
str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_REGEXPS
    MatchData mdata;
    JSBool ok;

    mdata.base.optarg = 1;
    mdata.base.mode = GLOB_MATCH;
    mdata.arrayobj = NULL;
    if (!js_AddRoot(cx, &mdata.arrayobj, "mdata.arrayobj"))
	return JS_FALSE;
    ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval);
    if (ok && mdata.arrayobj)
	*rval = OBJECT_TO_JSVAL(mdata.arrayobj);
    js_RemoveRoot(cx, &mdata.arrayobj);
    return ok;
#else
    return str_nyi(cx, "match");
#endif
}

static JSBool
str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_REGEXPS
    MatchData mdata;

    mdata.base.optarg = 1;
    mdata.base.mode = GLOB_SEARCH;
    return match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval);
#else
    return str_nyi(cx, "search");
#endif
}

#if JS_HAS_REGEXPS
typedef struct ReplaceData {
    GlobData    base;           /* base struct state */
    JSObject    *lambda;        /* replacement function object or null */
    JSString    *repstr;        /* replacement string */
    jschar      *dollar;        /* null or pointer to first $ in repstr */
    jschar      *chars;         /* result chars, null initially */
    size_t      length;         /* result length, 0 initially */
    jsint       index;          /* index in result of next replacement */
    jsint       leftIndex;      /* left context index in base.str->chars */
} ReplaceData;

static JSSubString *
interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip)
{
    JSRegExpStatics *res;
    jschar dc, *cp;
    uintN num, tmp;
    JSString *str;

    /* Allow a real backslash (literal "\\") to escape "$1" etc. */
    JS_ASSERT(*dp == '$');
    if (dp > rdata->repstr->chars && dp[-1] == '\\')
	return NULL;

    /* Interpret all Perl match-induced dollar variables. */
    res = &cx->regExpStatics;
    dc = dp[1];
    if (JS7_ISDEC(dc)) {
	if (dc == '0')
	    return NULL;

	/* Check for overflow to avoid gobbling arbitrary decimal digits. */
	num = 0;
	cp = dp;
	while ((dc = *++cp) != 0 && JS7_ISDEC(dc)) {
	    tmp = 10 * num + JS7_UNDEC(dc);
	    if (tmp < num)
		break;
	    num = tmp;
	}

	/* Adjust num from 1 $n-origin to 0 array-index-origin. */
	num--;
	*skip = cp - dp;
	return REGEXP_PAREN_SUBSTRING(res, num);
    }

    *skip = 2;
    switch (dc) {
      case '&':
	return &res->lastMatch;
      case '+':
	return &res->lastParen;
      case '`':
	if (cx->version == JSVERSION_1_2) {
	    /*
	     * JS1.2 imitated the Perl4 bug where left context at each step
	     * in an iterative use of a global regexp started from last match,
	     * not from the start of the target string.  But Perl4 does start
	     * $` at the beginning of the target string when it is used in a
	     * substitution, so we emulate that special case here.
	     */
	    str = rdata->base.str;
	    res->leftContext.chars = str->chars;
	    res->leftContext.length = res->lastMatch.chars - str->chars;
	}
	return &res->leftContext;
      case '\'':
	return &res->rightContext;
    }
    return NULL;
}

static JSBool
find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
{
    JSString *repstr;
    size_t replen, skip;
    jschar *dp;
    JSSubString *sub;
#if JS_HAS_REPLACE_LAMBDA
    JSObject *lambda;

    lambda = rdata->lambda;
    if (lambda) {
	uintN argc, i, j, m, n, p;
	jsval *sp, *oldsp, rval;
	void *mark;
	JSStackFrame *fp;
	JSBool ok;

	/*
	 * In the lambda case, not only do we find the replacement string's
	 * length, we compute repstr and return it via rdata for use within
	 * do_replace.  The lambda is called with arguments ($&, $1, $2, ...,
	 * index, input), i.e., all the properties of a regexp match array.
	 * For $&, etc., we must create string jsvals from cx->regExpStatics.
	 * We grab up stack space to keep the newborn strings GC-rooted.
	 */
	p = rdata->base.regexp->parenCount;
	argc = 1 + p + 2;
	sp = js_AllocStack(cx, 2 + argc, &mark);
	if (!sp)
	    return JS_FALSE;

	/* Push lambda and its 'this' parameter. */
	*sp++ = OBJECT_TO_JSVAL(lambda);
	*sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));

#define PUSH_REGEXP_STATIC(sub)                                               \
    JS_BEGIN_MACRO                                                            \
	JSString *str = js_NewStringCopyN(cx,                                 \
					  cx->regExpStatics.sub.chars,        \
					  cx->regExpStatics.sub.length,       \
					  0);                                 \
	if (!str) {                                                           \
	    ok = JS_FALSE;                                                    \
	    goto lambda_out;                                                  \
	}                                                                     \
	*sp++ = STRING_TO_JSVAL(str);                                         \
    JS_END_MACRO

	/* Push $&, $1, $2, ... */
	PUSH_REGEXP_STATIC(lastMatch);
	i = 0;
	m = cx->regExpStatics.parenCount;
	n = JS_MIN(m, 9);
	for (j = 0; i < n; i++, j++)
	    PUSH_REGEXP_STATIC(parens[j]);
	for (j = 0; i < m; i++, j++)
	    PUSH_REGEXP_STATIC(moreParens[j]);

#undef PUSH_REGEXP_STATIC

	/* Make sure to push undefined for any unmatched parens. */
	for (; i < p; i++)
	    *sp++ = JSVAL_VOID;

	/* Push match index and input string. */
	*sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
	*sp++ = STRING_TO_JSVAL(rdata->base.str);

	/* Lift current frame to include the args and do the call. */
	fp = cx->fp;
	oldsp = fp->sp;
	fp->sp = sp;
	ok = js_Invoke(cx, argc, JS_FALSE);
	rval = fp->sp[-1];
	fp->sp = oldsp;

	if (ok) {
	    /*
	     * NB: we count on the newborn string root to hold any string
	     * created by this js_ValueToString that would otherwise be GC-
	     * able, until we use rdata->repstr in do_replace.
	     */
	    repstr = js_ValueToString(cx, rval);
	    if (!repstr) {
		ok = JS_FALSE;
	    } else {
		rdata->repstr = repstr;
		*sizep = repstr->length;
	    }
	}

      lambda_out:
	js_FreeStack(cx, mark);
	return ok;
    }
#endif /* JS_HAS_REPLACE_LAMBDA */

    repstr = rdata->repstr;
    replen = repstr->length;
    for (dp = rdata->dollar; dp; dp = js_strchr(dp + 1, '$')) {
	sub = interpret_dollar(cx, dp, rdata, &skip);
	if (sub)
	    replen += sub->length - skip;
    }
    *sizep = replen;
    return JS_TRUE;
}

static void
do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars)
{
    JSString *repstr;
    jschar *cp, *dp;
    size_t len, skip;
    JSSubString *sub;

    repstr = rdata->repstr;
    cp = repstr->chars;
    dp = rdata->dollar;
    while (dp) {
	len = dp - cp;
	js_strncpy(chars, cp, len);
	chars += len;
	cp = dp;
	sub = interpret_dollar(cx, dp, rdata, &skip);
	if (sub) {
	    len = sub->length;
	    js_strncpy(chars, sub->chars, len);
	    chars += len;
	    cp += skip;
	}
	dp = js_strchr(dp + 1, '$');
    }
    js_strncpy(chars, cp, repstr->length - (cp - repstr->chars));
}

static JSBool
replace_glob(JSContext *cx, jsint count, GlobData *data)
{
    ReplaceData *rdata;
    JSString *str;
    size_t leftoff, leftlen, replen, growth;
    const jschar *left;
    jschar *chars;

    rdata = (ReplaceData *)data;
    str = data->str;
    leftoff = rdata->leftIndex;
    left = str->chars + leftoff;
    leftlen = cx->regExpStatics.lastMatch.chars - left;
    rdata->leftIndex = cx->regExpStatics.lastMatch.chars - str->chars;
    rdata->leftIndex += cx->regExpStatics.lastMatch.length;
    if (!find_replen(cx, rdata, &replen))
	return JS_FALSE;
    growth = leftlen + replen;
    chars = rdata->chars
	    ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1)
					   * sizeof(jschar))
	    : JS_malloc(cx, (growth + 1) * sizeof(jschar));
    if (!chars) {
	JS_free(cx, rdata->chars);
	rdata->chars = NULL;
	return JS_FALSE;
    }
    rdata->chars = chars;
    rdata->length += growth;
    chars += rdata->index;
    rdata->index += growth;
    js_strncpy(chars, left, leftlen);
    chars += leftlen;
    do_replace(cx, rdata, chars);
    return JS_TRUE;
}
#endif /* JS_HAS_REGEXPS */

static JSBool
str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_REGEXPS
    JSObject *lambda;
    JSString *repstr, *str;
    ReplaceData rdata;
    jschar *chars;
    size_t leftlen, rightlen, length;

#if JS_HAS_REPLACE_LAMBDA
    if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) {
	lambda = JSVAL_TO_OBJECT(argv[1]);
	repstr = NULL;
    } else
#endif
    {
	if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1]))
	    return JS_FALSE;
	repstr = JSVAL_TO_STRING(argv[1]);
	lambda = NULL;
    }

    rdata.base.optarg = 2;
    rdata.base.mode = GLOB_REPLACE;
    rdata.lambda = lambda;
    rdata.repstr = repstr;
    rdata.dollar = repstr ? js_strchr(repstr->chars, '$') : NULL;
    rdata.chars = NULL;
    rdata.length = 0;
    rdata.index = 0;
    rdata.leftIndex = 0;
    if (!match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval))
	return JS_FALSE;

    if (!rdata.chars) {
	if (rdata.base.global || *rval != JSVAL_TRUE) {
	    /* Didn't match even once. */
	    *rval = STRING_TO_JSVAL(rdata.base.str);
	    return JS_TRUE;
	}
	leftlen = cx->regExpStatics.leftContext.length;
	if (!find_replen(cx, &rdata, &length))
	    return JS_FALSE;
	length += leftlen;
	chars = JS_malloc(cx, (length + 1) * sizeof(jschar));
	if (!chars)
	    return JS_FALSE;
	js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen);
	do_replace(cx, &rdata, chars + leftlen);
	rdata.chars = chars;
	rdata.length = length;
    }

    rightlen = cx->regExpStatics.rightContext.length;
    length = rdata.length + rightlen;
    chars = JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar));
    if (!chars) {
	JS_free(cx, rdata.chars);
	return JS_FALSE;
    }
    js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars,
	       rightlen);
    chars[length] = 0;

    str = js_NewString(cx, chars, length, 0);
    if (!str) {
	JS_free(cx, chars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
#else
    return str_nyi(cx, "replace");
#endif
}

/*
 * Subroutine used by str_split to find the next split point in str, starting
 * at offset *ip and looking either for the separator substring given by sep,
 * or for the next re match.  In the re case, return the matched separator in
 * *sep, and the possibly updated offset in *ip.
 *
 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
 * separator occurrence if found, or str->length if no separator is found.
 */
static jsint
find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip,
	   JSSubString *sep)
{
    jsint i, j, k;

    /*
     * Stop if past end of string.  If at end of string, we will compare the
     * null char stored there (by js_NewString*) to sep->chars[j] in the while
     * loop at the end of this function, so that
     *
     *  "ab,".split(',') => ["ab", ""]
     *
     * and the resulting array converts back to the string "ab," for symmetry.
     * However, we ape Perl and do this only if there is a sufficiently large
     * limit argument (see str_split).
     */
    i = *ip;
    if ((size_t)i > str->length)
	return -1;

    /*
     * Perl4 special case for str.split(' '), only if the user has selected
     * JavaScript1.2 explicitly.  Split on whitespace, and skip leading w/s.
     * Strange but true, apparently modeled after awk.
     *
     * NB: we set sep->length to the length of the w/s run, so we must test
     * sep->chars[1] == 0 to make sure sep is just one space.
     */
    if (cx->version == JSVERSION_1_2 &&
	!re && *sep->chars == ' ' && sep->chars[1] == 0) {
	/* Skip leading whitespace if at front of str. */
	if (i == 0) {
	    while (JS_ISSPACE(str->chars[i]))
		i++;
	    *ip = i;
	}

	/* Don't delimit whitespace at end of string. */
	if ((size_t)i == str->length)
	    return -1;

	/* Skip over the non-whitespace chars. */
	while ((size_t)i < str->length && !JS_ISSPACE(str->chars[i]))
	    i++;

	/* Now skip the next run of whitespace. */
	j = i;
	while ((size_t)j < str->length && JS_ISSPACE(str->chars[j]))
	    j++;

	/* Update sep->length to count delimiter chars. */
	sep->length = (size_t)(j - i);
	return i;
    }

#if JS_HAS_REGEXPS
    /*
     * Match a regular expression against the separator at or above index i.
     * Call js_ExecuteRegExp with true for the test argument.  On successful
     * match, get the separator from cx->regExpStatics.lastMatch.
     */
    if (re) {
	size_t index;
	jsval rval;

      again:
	/* JS1.2 deviated from Perl by never matching at end of string. */
	index = (size_t)i;
	if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval))
	    return -2;
	if (rval != JSVAL_TRUE) {
	    /* Mismatch: ensure our caller advances i past end of string. */
	    sep->length = 1;
	    return str->length;
	}
	i = (jsint)index;
	*sep = cx->regExpStatics.lastMatch;
	if (sep->length == 0) {
	    /*
	     * Empty string match: never split on an empty match at the start
	     * of a find_split cycle.  Same rule as for an empty global match
	     * in match_or_replace.
	     */
	    if (i == *ip) {
		/*
		 * "Bump-along" to avoid sticking at an empty match, but don't
		 * bump past end of string -- our caller must do that by adding
		 * sep->length to our return value.
		 */
		if ((size_t)i == str->length) {
		    sep->length = 1;
		    return i;
		}
		i++;
		goto again;
	    }
	}
	JS_ASSERT((size_t)i >= sep->length);
	return i - sep->length;
    }
#endif

    /*
     * Deviate from ECMA by never splitting an empty string by any separator
     * string into a non-empty array (an array of length 1 that contains the
     * empty string).
     */
    if (!JSVERSION_IS_ECMA(cx->version) && str->length == 0)
	return -1;

    /*
     * Special case: if sep is the empty string, split str into one character
     * substrings.  Let our caller worry about whether to split once at end of
     * string into an empty substring.
     *
     * For 1.2 compatibility, at the end of the string, we return the string length
     * as the result, and set the separator length to 1 - this allows the caller
     * to include an additional null string at the end of the substring list.
     */
    if (sep->length == 0)
        if (cx->version == JSVERSION_1_2) {
            if ((size_t)i == str->length) {
                sep->length = 1;
                return i;
            }
            else
                return i + 1;
        }
        else
            return ((size_t)i == str->length) ? -1 : i + 1;

    /*
     * Now that we know sep is non-empty, search starting at i in str for an
     * occurrence of all of sep's chars.  If we find them, return the index of
     * the first separator char.  Otherwise, return str->length.
     */
    j = 0;
    while ((size_t)(k = i + j) < str->length) {
	if (str->chars[k] == sep->chars[j]) {
	    if ((size_t)++j == sep->length)
		return i;
	} else {
	    i++;
	    j = 0;
	}
    }
    return k;
}

static JSBool
str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str, *sub;
    JSObject *arrayobj, *reobj;
    jsval v;
    JSBool ok, limited;
    JSRegExp *re;
    JSSubString *sep, tmp;
    jsdouble d;
    jsint len, limit, i, j, sublen;
    const jschar *substr;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL);
    if (!arrayobj)
	return JS_FALSE;
    *rval = OBJECT_TO_JSVAL(arrayobj);

    if (argc == 0) {
	v = STRING_TO_JSVAL(str);
	ok = JS_SetElement(cx, arrayobj, 0, &v);
    } else {
#if JS_HAS_REGEXPS
	if (JSVAL_IS_REGEXP(cx, argv[0])) {
	    reobj = JSVAL_TO_OBJECT(argv[0]);
	    re = JS_GetPrivate(cx, reobj);
	    sep = &tmp;

	    /* Set a magic value so we can detect a successful re match. */
	    sep->chars = NULL;
	} else
#endif
	{
	    sep = (JSSubString *)js_ValueToString(cx, argv[0]);
	    if (!sep)
		return JS_FALSE;
	    argv[0] = STRING_TO_JSVAL(sep);
	    reobj = NULL;
	    re = NULL;
	}

	/* Use the second argument as the split limit, if given. */
	/* XXX our v2 ecma spec checks against given, undefined. */
	limited = (argc > 1);
        limit = 0; /* Avoid warning. */
	if (limited) {
	    if (!js_ValueToNumber(cx, argv[1], &d))
		return JS_FALSE;

	    /* Clamp limit between 0 and 1 + string length. */
	    d = js_DoubleToInteger(d);
	    if (d < 0)
		d = 0;
	    else if (d > str->length)
		d = 1 + str->length;
	    limit = (jsint)d;
	}

	if (reobj) {
	    /* Lock to protect re just in case it's shared and global. */
	    JS_LOCK_OBJ(cx, reobj);
	}

	len = i = 0;
	while ((j = find_split(cx, str, re, &i, sep)) >= 0) {
	    if (limited && len >= limit)
		break;
	    sublen = j - i;
	    substr = str->chars + i;
	    sub = js_NewStringCopyN(cx, substr, (size_t)sublen, 0);
	    if (!sub) {
		ok = JS_FALSE;
		goto unlock_reobj;
	    }
	    v = STRING_TO_JSVAL(sub);
	    ok = JS_SetElement(cx, arrayobj, len, &v);
	    if (!ok)
		goto unlock_reobj;
	    len++;
#if JS_HAS_REGEXPS
	    /*
	     * Imitate perl's feature of including parenthesized substrings
	     * that matched part of the delimiter in the new array, after the
	     * split substring that was delimited.
	     */
	    if (re && sep->chars) {
		uintN num;
		JSSubString *parsub;

		for (num = 0; num < cx->regExpStatics.parenCount; num++) {
		    if (limited && len >= limit)
			break;
		    parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
		    sub = js_NewStringCopyN(cx, parsub->chars, parsub->length,
					    0);
		    if (!sub) {
			ok = JS_FALSE;
			goto unlock_reobj;
		    }
		    v = STRING_TO_JSVAL(sub);
		    ok = JS_SetElement(cx, arrayobj, len, &v);
		    if (!ok)
			goto unlock_reobj;
		    len++;
		}
		sep->chars = NULL;
	    }
#endif
	    i = j + sep->length;
	    if (!JSVERSION_IS_ECMA(cx->version)) {
		/*
		 * Deviate from ECMA to imitate Perl, which omits a final
		 * split unless a limit argument is given and big enough.
		 */
		if (!limited && (size_t)i == str->length)
		    break;
	    }
	}
	ok = (j != -2);
      unlock_reobj:
	if (reobj)
	    JS_UNLOCK_OBJ(cx, reobj);
    }
    return ok;
}

static JSBool
str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
    JSString *str;
    jsdouble d;
    jsdouble length, begin, end;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;

    if (argc != 0) {
	if (!js_ValueToNumber(cx, argv[0], &d))
	    return JS_FALSE;
	length = str->length;
	begin = js_DoubleToInteger(d);
	if (begin < 0) {
	    begin += length;
	    if (begin < 0)
		begin = 0;
	} else if (begin > length) {
	    begin = length;
	}

	if (argc == 1) {
	    end = length;
	} else {
	    if (!js_ValueToNumber(cx, argv[1], &d))
		return JS_FALSE;
	    end = js_DoubleToInteger(d);
	    if (end < 0)
		end = 0;
	    end += begin;
	    if (end > length)
		end = length;
	}

	str = js_NewStringCopyN(cx, str->chars + (size_t)begin,
				(size_t)(end - begin), 0);
	if (!str)
	    return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
#else
    return str_nyi(cx, "substr");
#endif
}

#ifdef NOTYET
/*
 * From "Programming perl", Larry Wall and Randall L. Schwartz, Copyright XXX
 * O'Reilly & Associates, Inc., but with Java primitive type sizes for i, l,
 * and so on:
 *
 *  a   An ASCII string, unstripped.
 *  A   An ASCII string, trailing nulls and spaces will be stripped.
 *  b   A bit string, low-to-high order.
 *  B   A bit string, high-to-low order.
 *  h   A hexadecimal string, low nybble first.
 *  H   A hexadecimal string, high nybble first.
 *  c   A signed char value.
 *  C   An unsigned char value.
 *  s   A signed short (16-bit) value.
 *  S   An unsigned short (16-bit) value.
 *  i   A signed integer (32-bit) value.
 *  I   An unsigned integer (32-bit) value.
 *  l   A signed long (64-bit) value.
 *  L   An unsigned long (64-bit) value.
 *  n   A short in "network" byte order.
 *  N   An integer in "network" byte order.
 *  f   A single-precision float in IEEE format.
 *  d   A double-precision float in IEEE format.
 *  p   A pointer to a string.
 *  x   Skip forward a byte.
 *  X   Back up one byte.
 *  @   Go to absolute position in string for next field.
 *  u   Uudecode a string.
 *
 * Each letter may be followed by a number giving the repeat count.  Together
 * the letter and repeat count make a field specifier.  Field specifiers may
 * be separated by whitespace, which will be ignored.
 */
static JSBool
str_unpack(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
#if JS_HAS_MORE_PERL_FUN
#else
    return str_nyi(cx, "unpack");
#endif
}
#endif /* NOTYET */

#if JS_HAS_SEQUENCE_OPS
/*
 * Python-esque sequence operations.
 */
static JSBool
str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str, *str2;
    JSBool ok;
    size_t length, length2, newlength;
    jschar *chars, *newchars;
    uintN i;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    length = str->length;
    chars = JS_malloc(cx, (length + 1) * sizeof(jschar));
    if (!chars)
	return JS_FALSE;
    js_strncpy(chars, str->chars, length);

    ok = JS_TRUE;
    for (i = 0; i < argc; i++) {
	str2 = js_ValueToString(cx, argv[i]);
	if (!str2) {
	    ok = JS_FALSE;
	    goto out;
	}
	length2 = str2->length;
	newlength = length + length2;
	newchars = JS_realloc(cx, chars, (newlength + 1) * sizeof(jschar));
	if (!newchars) {
	    ok = JS_FALSE;
	    goto out;
	}
	chars = newchars;
	js_strncpy(chars + length, str2->chars, length2);
	length = newlength;
    }

    chars[length] = 0;
    str = js_NewString(cx, chars, length, 0);
    if (!str)
	ok = JS_FALSE;
out:
    if (ok)
	*rval = STRING_TO_JSVAL(str);
    else
	JS_free(cx, chars);
    return ok;
}

static JSBool
str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;
    jsdouble d;
    jsdouble length, begin, end;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    if (argc != 0) {
	if (!js_ValueToNumber(cx, argv[0], &d))
	    return JS_FALSE;
	length = str->length;
	begin = js_DoubleToInteger(d);
	if (begin < 0) {
	    begin += length;
	    if (begin < 0)
		begin = 0;
	} else if (begin > length) {
	    begin = length;
	}

	if (argc == 1) {
	    end = length;
	} else {
	    if (!js_ValueToNumber(cx, argv[1], &d))
		return JS_FALSE;
	    end = js_DoubleToInteger(d);
	    if (end < 0) {
		end += length;
		if (end < 0)
		    end = 0;
	    } else if (end > length) {
		end = length;
	    }
	    if (end < begin)
		end = begin;
	}

	str = js_NewStringCopyN(cx, str->chars + (size_t)begin,
				(size_t)(end - begin), 0);
	if (!str)
	    return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
#endif /* JS_HAS_SEQUENCE_OPS */

/*
 * HTML composition aids.
 */
static JSBool
tagify(JSContext *cx, JSObject *obj, jsval *argv,
       const char *begin, const jschar *param, const char *end,
       jsval *rval)
{
    JSString *str;
    jschar *tagbuf;
    size_t beglen, endlen, parlen, taglen;
    size_t i, j;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
    if (!str)
	return JS_FALSE;
    argv[-1] = STRING_TO_JSVAL(str);

    if (!end)
	end = begin;

    beglen = strlen(begin);
    taglen = 1 + beglen + 1;			/* '<begin' + '>' */
    parlen = 0; /* Avoid warning. */
    if (param) {
	parlen = js_strlen(param);
	taglen += 2 + parlen + 1;		/* '="param"' */
    }
    endlen = strlen(end);
    taglen += str->length + 2 + endlen + 1;	/* 'str</end>' */

    tagbuf = JS_malloc(cx, (taglen + 1) * sizeof(jschar));
    if (!tagbuf)
	return JS_FALSE;

    j = 0;
    tagbuf[j++] = '<';
    for (i = 0; i < beglen; i++)
	tagbuf[j++] = (jschar)begin[i];
    if (param) {
	tagbuf[j++] = '=';
	tagbuf[j++] = '"';
	js_strncpy(&tagbuf[j], param, parlen);
	j += parlen;
	tagbuf[j++] = '"';
    }
    tagbuf[j++] = '>';
    js_strncpy(&tagbuf[j], str->chars, str->length);
    j += str->length;
    tagbuf[j++] = '<';
    tagbuf[j++] = '/';
    for (i = 0; i < endlen; i++)
	tagbuf[j++] = (jschar)end[i];
    tagbuf[j++] = '>';
    JS_ASSERT(j == taglen);

    str = js_NewString(cx, tagbuf, taglen, 0);
    if (!str) {
	free((char *)tagbuf);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSBool
tagify_value(JSContext *cx, JSObject *obj, jsval *argv,
	     const char *begin, const char *end,
	     jsval *rval)
{
    JSString *param;

    param = js_ValueToString(cx, argv[0]);
    if (!param)
	return JS_FALSE;
    argv[0] = STRING_TO_JSVAL(param);
    return tagify(cx, obj, argv, begin, param->chars, end, rval);
}

static JSBool
str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "B", NULL, NULL, rval);
}

static JSBool
str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "I", NULL, NULL, rval);
}

static JSBool
str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "TT", NULL, NULL, rval);
}

static JSBool
str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify_value(cx, obj, argv, "FONT SIZE", "FONT", rval);
}

static JSBool
str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
	      jsval *rval)
{
    return tagify_value(cx, obj, argv, "FONT COLOR", "FONT", rval);
}

static JSBool
str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify_value(cx, obj, argv, "A HREF", "A", rval);
}

static JSBool
str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify_value(cx, obj, argv, "A NAME", "A", rval);
}

static JSBool
str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "STRIKE", NULL, NULL, rval);
}

static JSBool
str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "SMALL", NULL, NULL, rval);
}

static JSBool
str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "BIG", NULL, NULL, rval);
}

static JSBool
str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "BLINK", NULL, NULL, rval);
}

static JSBool
str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "SUP", NULL, NULL, rval);
}

static JSBool
str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    return tagify(cx, obj, argv, "SUB", NULL, NULL, rval);
}

static JSFunctionSpec string_methods[] = {
#if JS_HAS_TOSOURCE
    {"quote",           str_quote,              0},
    {js_toSource_str,   str_toSource,           0},
#endif

    /* Java-like methods. */
    {js_toString_str,   str_toString,           0},
    {js_valueOf_str,    str_valueOf,            0},
    {"substring",       str_substring,          2},
    {"toLowerCase",     str_toLowerCase,        0},
    {"toUpperCase",     str_toUpperCase,        0},
    {"charAt",          str_charAt,             1},
    {"charCodeAt",      str_charCodeAt,         1},
    {"indexOf",         str_indexOf,            2},
    {"lastIndexOf",     str_lastIndexOf,        2},

    /* Perl-ish methods (search is actually Python-esque). */
    {"match",           str_match,              1},
    {"search",          str_search,             1},
    {"replace",         str_replace,            2},
    {"split",           str_split,              1},
    {"substr",          str_substr,             2},
#ifdef NOTYET
    {"unpack",          str_unpack,             1},
#endif

    /* Python-esque sequence methods. */
#if JS_HAS_SEQUENCE_OPS
    {"concat",          str_concat,             0},
    {"slice",           str_slice,              0},
#endif

    /* HTML string methods. */
    {"bold",            str_bold,               0},
    {"italics",         str_italics,            0},
    {"fixed",           str_fixed,              0},
    {"fontsize",        str_fontsize,           1},
    {"fontcolor",       str_fontcolor,          1},
    {"link",            str_link,               1},
    {"anchor",          str_anchor,             1},
    {"strike",          str_strike,             0},
    {"small",           str_small,              0},
    {"big",             str_big,                0},
    {"blink",           str_blink,              0},
    {"sup",             str_sup,                0},
    {"sub",             str_sub,                0},
    {0}
};

static JSBool
String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *str;

    if (argc > 0) {
	str = js_ValueToString(cx, argv[0]);
	if (!str)
	    return JS_FALSE;
    } else {
	str = cx->runtime->emptyString;
    }
    if (!cx->fp->constructing) {
	*rval = STRING_TO_JSVAL(str);
	return JS_TRUE;
    }
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));
    return JS_TRUE;
}

static JSBool
str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		 jsval *rval)
{
    jschar *chars;
    uintN i;
    uint16 code;
    JSString *str;

    chars = JS_malloc(cx, (argc + 1) * sizeof(jschar));
    if (!chars)
	return JS_FALSE;
    for (i = 0; i < argc; i++) {
	if (!js_ValueToUint16(cx, argv[i], &code)) {
	    JS_free(cx, chars);
	    return JS_FALSE;
	}
	chars[i] = (jschar)code;
    }
    chars[i] = 0;
    str = js_NewString(cx, chars, argc, 0);
    if (!str) {
	JS_free(cx, chars);
	return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}

static JSFunctionSpec string_static_methods[] = {
    {"fromCharCode",    str_fromCharCode,       1},
    {0}
};

static JSHashTable *deflated_string_cache;
static uint32 deflated_string_cache_bytes;
#ifdef JS_THREADSAFE
static JSLock *deflated_string_cache_lock;
#endif

JSBool
js_InitStringGlobals(void)
{
#ifdef JS_THREADSAFE
    /* Must come through here once in primordial thread to init safely! */
    if (!deflated_string_cache_lock) {
	deflated_string_cache_lock = JS_NEW_LOCK();
	if (!deflated_string_cache_lock)
	    return JS_FALSE;
    }
#endif
    return JS_TRUE;
}

void
js_FreeStringGlobals()
{
    if (deflated_string_cache) {
	JS_HashTableDestroy(deflated_string_cache);
	deflated_string_cache = NULL;
    }
#ifdef JS_THREADSAFE
    if (deflated_string_cache_lock) {
	JS_DESTROY_LOCK(deflated_string_cache_lock);
	deflated_string_cache_lock = NULL;
    }
#endif
}

JSObject *
js_InitStringClass(JSContext *cx, JSObject *obj)
{
    JSRuntime *rt;
    JSString *empty;
    JSObject *proto;

    rt = cx->runtime;
    empty = rt->emptyString;
    if (!empty) {
	/* Make a permanently locked empty string. */
	empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK);
	if (!empty)
	    return NULL;
	rt->emptyString = empty;
    }

    /* Define the escape, unescape functions in the global object. */
    if (!JS_DefineFunctions(cx, obj, string_functions))
	return NULL;

    proto = JS_InitClass(cx, obj, NULL, &string_class, String, 1,
			 string_props, string_methods,
			 NULL, string_static_methods);
    if (!proto)
	return NULL;
    OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, STRING_TO_JSVAL(empty));
    return proto;
}

JSString *
js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag)
{
    JSString *str;

    str = js_AllocGCThing(cx, gcflag | GCX_STRING);
    if (!str)
	return NULL;
    str->length = length;
    str->chars = chars;
    return str;
}

JSString *
js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag)
{
    jschar *news;
    JSString *str;

    news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar));
    if (!news)
	return NULL;
    js_strncpy(news, s, n);
    news[n] = 0;
    str = js_NewString(cx, news, n, gcflag);
    if (!str)
	JS_free(cx, news);
    return str;
}

JSString *
js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag)
{
    size_t n, m;
    jschar *news;
    JSString *str;

    n = js_strlen(s);
    m = (n + 1) * sizeof(jschar);
    news = JS_malloc(cx, m);
    if (!news)
	return NULL;
    memcpy(news, s, m);
    str = js_NewString(cx, news, js_strlen(news), gcflag);
    if (!str)
	JS_free(cx, news);
    return str;
}

JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_string_pointer(const void *key)
{
    return (JSHashNumber)key >> JSVAL_TAGBITS;
}

void
js_FinalizeString(JSContext *cx, JSString *str)
{
    JSHashNumber hash;
    JSHashEntry *he, **hep;

    if (str->chars) {
	JS_free(cx, str->chars);
	str->chars = NULL;
	if (deflated_string_cache) {
	    hash = js_hash_string_pointer(str);
	    JS_ACQUIRE_LOCK(deflated_string_cache_lock);
	    hep = JS_HashTableRawLookup(deflated_string_cache, hash, str);
	    he = *hep;
	    if (he) {
		JS_free(cx, he->value);
		JS_HashTableRawRemove(deflated_string_cache, hep, he);
		deflated_string_cache_bytes -= str->length;
	    }
	    JS_RELEASE_LOCK(deflated_string_cache_lock);
	}
    }
    str->length = 0;
}

JSObject *
js_StringToObject(JSContext *cx, JSString *str)
{
    JSObject *obj;

    obj = js_NewObject(cx, &string_class, NULL, NULL);
    if (!obj)
	return NULL;
    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));
    return obj;
}

JSString *
js_ValueToString(JSContext *cx, jsval v)
{
    JSObject *obj;
    JSString *str;

    if (JSVAL_IS_OBJECT(v)) {
	obj = JSVAL_TO_OBJECT(v);
	if (!obj)
	    return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
	if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))
	    return NULL;
    }
    if (JSVAL_IS_STRING(v)) {
	str = JSVAL_TO_STRING(v);
    } else if (JSVAL_IS_INT(v)) {
	str = js_NumberToString(cx, JSVAL_TO_INT(v));
    } else if (JSVAL_IS_DOUBLE(v)) {
	str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v));
    } else if (JSVAL_IS_BOOLEAN(v)) {
	str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v));
    } else {
	str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
    }
    return str;
}

JSString *
js_ValueToSource(JSContext *cx, jsval v)
{
    if (JSVAL_IS_STRING(v))
	return js_QuoteString(cx, JSVAL_TO_STRING(v), '"');
    if (!JSVAL_IS_PRIMITIVE(v)) {
	if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v),
		     cx->runtime->atomState.toSourceAtom,
		     0, NULL, &v))
            return NULL;
    }
    return js_ValueToString(cx, v);
}

JSHashNumber
js_HashString(const JSString *str)
{
    JSHashNumber h;
    size_t n, m;
    const jschar *s;

    h = 0;
    n = str->length;
    s = str->chars;
    if (n < 16) {
	/* Hash every char in a short string. */
	for (; n; s++, n--)
	    h = (h >> 28) ^ (h << 4) ^ *s;
    } else {
	/* Sample a la java.lang.String.hash(). */
	for (m = n / 8; n >= m; s += m, n -= m)
	    h = (h >> 28) ^ (h << 4) ^ *s;
    }
    return h;
}

intN
js_CompareStrings(const JSString *str1, const JSString *str2)
{
    size_t l1, l2, n, i;
    const jschar *s1, *s2;
    intN cmp;

    l1 = str1->length, l2 = str2->length;
    s1 = str1->chars,  s2 = str2->chars;
    n = JS_MIN(l1, l2);
    for (i = 0; i < n; i++) {
	cmp = s1[i] - s2[i];
	if (cmp != 0)
	    return cmp;
    }
    return (intN)(l1 - l2);
}

size_t
js_strlen(const jschar *s)
{
    const jschar *t;

    for (t = s; *t != 0; t++)
	;
    return (size_t)(t - s);
}

jschar *
js_strchr(const jschar *s, jschar c)
{
    while (*s != 0) {
	if (*s == c)
	    return (jschar *)s;
	s++;
    }
    return NULL;
}

const jschar *
js_SkipWhiteSpace(const jschar *s)
{
    /* JS_ISSPACE is false on a null. */
    while (JS_ISSPACE(*s))
	s++;
    return s;
}

jschar *
js_strncpy(jschar *t, const jschar *s, size_t n)
{
    size_t i;

    for (i = 0; i < n; i++)
	t[i] = s[i];
    return t;
}

jschar *
js_InflateString(JSContext *cx, const char *bytes, size_t length)
{
    jschar *chars;
    size_t i;

    chars = JS_malloc(cx, (length + 1) * sizeof(jschar));
    if (!chars)
	return NULL;
    for (i = 0; i < length; i++)
	chars[i] = (unsigned char) bytes[i];
    chars[i] = 0;
    return chars;
}

/*
 * May be called with null cx by js_GetStringBytes, see below.
 */
char *
js_DeflateString(JSContext *cx, const jschar *chars, size_t length)
{
    size_t i, size;
    char *bytes;

    size = (length + 1) * sizeof(char);
    bytes = cx ? JS_malloc(cx, size) : malloc(size);
    if (!bytes)
	return NULL;
    for (i = 0; i < length; i++)
	bytes[i] = (char) chars[i];
    bytes[i] = 0;
    return bytes;
}

static JSHashTable *
GetDeflatedStringCache(void)
{
    JSHashTable *cache;

    cache = deflated_string_cache;
    if (!cache) {
	cache = JS_NewHashTable(8, js_hash_string_pointer,
				JS_CompareValues, JS_CompareValues,
				NULL, NULL);
	deflated_string_cache = cache;
    }
    return cache;
}

JSBool
js_SetStringBytes(JSString *str, char *bytes, size_t length)
{
    JSHashTable *cache;
    JSBool ok;
    JSHashNumber hash;
    JSHashEntry **hep;

    JS_ACQUIRE_LOCK(deflated_string_cache_lock);

    cache = GetDeflatedStringCache();
    if (!cache) {
	ok = JS_FALSE;
    } else {
	hash = js_hash_string_pointer(str);
	hep = JS_HashTableRawLookup(cache, hash, str);
	JS_ASSERT(*hep == NULL);
	ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL;
	if (ok)
	    deflated_string_cache_bytes += length;
    }

    JS_RELEASE_LOCK(deflated_string_cache_lock);
    return ok;
}

char *
js_GetStringBytes(JSString *str)
{
    JSHashTable *cache;
    char *bytes;
    JSHashNumber hash;
    JSHashEntry *he, **hep;

    JS_ACQUIRE_LOCK(deflated_string_cache_lock);

    cache = GetDeflatedStringCache();
    if (!cache) {
	bytes = NULL;
    } else {
	hash = js_hash_string_pointer(str);
	hep = JS_HashTableRawLookup(cache, hash, str);
	he = *hep;
	if (he) {
	    bytes = he->value;
	} else {
	    bytes = js_DeflateString(NULL, str->chars, str->length);
	    if (bytes) {
		if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) {
		    deflated_string_cache_bytes += str->length;
		} else {
		    free(bytes);
		    bytes = NULL;
		}
	    }
	}
    }

    JS_RELEASE_LOCK(deflated_string_cache_lock);
    return bytes;
}

/*
 * From java.lang.Character.java:
 *
 * The character properties are currently encoded into 32 bits in the
 * following manner:
 *
 * 10 bits      signed offset used for converting case
 *  1 bit       if 1, adding the signed offset converts the character to
 *              lowercase
 *  1 bit       if 1, subtracting the signed offset converts the character to
 *              uppercase
 *  1 bit       if 1, character has a titlecase equivalent (possibly itself)
 *  3 bits      0  may not be part of an identifier
 *              1  ignorable control; may continue a Unicode identifier or JS
 *                 identifier
 *              2  may continue a JS identifier but not a Unicode identifier
 *                 (unused)
 *              3  may continue a Unicode identifier or JS identifier
 *              4  is a JS whitespace character
 *              5  may start or continue a JS identifier;
 *                 may continue but not start a Unicode identifier (_)
 *              6  may start or continue a JS identifier but not a Unicode
 *                 identifier ($)
 *              7  may start or continue a Unicode identifier or JS identifier
 *              Thus:
 *                 5, 6, 7 may start a JS identifier
 *                 1, 2, 3, 5, 6, 7 may continue a JS identifier
 *                 7 may start a Unicode identifier
 *                 1, 3, 5, 7 may continue a Unicode identifier
 *                 1 is ignorable within an identifier
 *                 4 is JS whitespace
 *  2 bits      0  this character has no numeric property
 *              1  adding the digit offset to the character code and then
 *                 masking with 0x1F will produce the desired numeric value
 *              2  this character has a "strange" numeric value
 *              3  a JS supradecimal digit: adding the digit offset to the
 *                 character code, then masking with 0x1F, then adding 10
 *                 will produce the desired numeric value
 *  5 bits      digit offset
 *  4 bits      reserved for future use
 *  5 bits      character type
 */

/* The X table has 1024 entries for a total of 1024 bytes. */

const uint8 js_X[] = {
  0,   1,   2,   3,   4,   5,   6,   7,  /*  0x0000 */
  8,   9,  10,  11,  12,  13,  14,  15,  /*  0x0200 */
 16,  17,  18,  19,  20,  21,  22,  23,  /*  0x0400 */
 24,  25,  26,  27,  28,  28,  28,  28,  /*  0x0600 */
 28,  28,  28,  28,  29,  30,  31,  32,  /*  0x0800 */
 33,  34,  35,  36,  37,  38,  39,  40,  /*  0x0A00 */
 41,  42,  43,  44,  45,  46,  28,  28,  /*  0x0C00 */
 47,  48,  49,  50,  51,  52,  53,  28,  /*  0x0E00 */
 28,  28,  54,  55,  56,  57,  58,  59,  /*  0x1000 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1200 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1400 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1600 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1800 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1A00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1C00 */
 60,  60,  61,  62,  63,  64,  65,  66,  /*  0x1E00 */
 67,  68,  69,  70,  71,  72,  73,  74,  /*  0x2000 */
 75,  75,  75,  76,  77,  78,  28,  28,  /*  0x2200 */
 79,  80,  81,  82,  83,  83,  84,  85,  /*  0x2400 */
 86,  85,  28,  28,  87,  88,  89,  28,  /*  0x2600 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2800 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2A00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2C00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2E00 */
 90,  91,  92,  93,  94,  56,  95,  28,  /*  0x3000 */
 96,  97,  98,  99,  83, 100,  83, 101,  /*  0x3200 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3400 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3600 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3800 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3A00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3C00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3E00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4000 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4200 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4400 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4600 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4800 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4A00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4C00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x4E00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5A00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5C00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5E00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6A00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6C00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6E00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7A00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7C00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7E00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8A00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8C00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8E00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9A00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9C00 */
 56,  56,  56,  56,  56,  56, 102,  28,  /*  0x9E00 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA000 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA200 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA400 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA600 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA800 */
 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xAA00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xAC00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xAE00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xBA00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xBC00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xBE00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC400 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC600 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC800 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xCA00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xCC00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xCE00 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xD000 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xD200 */
 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xD400 */
 56,  56,  56,  56,  56,  56, 103,  28,  /*  0xD600 */
104, 104, 104, 104, 104, 104, 104, 104,  /*  0xD800 */
104, 104, 104, 104, 104, 104, 104, 104,  /*  0xDA00 */
104, 104, 104, 104, 104, 104, 104, 104,  /*  0xDC00 */
104, 104, 104, 104, 104, 104, 104, 104,  /*  0xDE00 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE000 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE200 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE400 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE600 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE800 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xEA00 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xEC00 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xEE00 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF000 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF200 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF400 */
105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF600 */
105, 105, 105, 105,  56,  56,  56,  56,  /*  0xF800 */
106,  28,  28,  28, 107, 108, 109, 110,  /*  0xFA00 */
 56,  56,  56,  56, 111, 112, 113, 114,  /*  0xFC00 */
115, 116,  56, 117, 118, 119, 120, 121   /*  0xFE00 */
};

/* The Y table has 7808 entries for a total of 7808 bytes. */

const uint8 js_Y[] = {
  0,   0,   0,   0,   0,   0,   0,   0,  /*    0 */
  0,   1,   1,   1,   1,   1,   0,   0,  /*    0 */
  0,   0,   0,   0,   0,   0,   0,   0,  /*    0 */
  0,   0,   0,   0,   1,   1,   1,   1,  /*    0 */
  2,   3,   3,   3,   4,   3,   3,   3,  /*    0 */
  5,   6,   3,   7,   3,   8,   3,   3,  /*    0 */
  9,   9,   9,   9,   9,   9,   9,   9,  /*    0 */
  9,   9,   3,   3,   7,   7,   7,   3,  /*    0 */
  3,  10,  10,  10,  10,  10,  10,  10,  /*    1 */
 10,  10,  10,  10,  10,  10,  10,  10,  /*    1 */
 10,  10,  10,  10,  10,  10,  10,  10,  /*    1 */
 10,  10,  10,   5,   3,   6,  11,  12,  /*    1 */
 11,  13,  13,  13,  13,  13,  13,  13,  /*    1 */
 13,  13,  13,  13,  13,  13,  13,  13,  /*    1 */
 13,  13,  13,  13,  13,  13,  13,  13,  /*    1 */
 13,  13,  13,   5,   7,   6,   7,   0,  /*    1 */
  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */
  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */
  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */
  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */
 14,   3,   4,   4,   4,   4,  15,  15,  /*    2 */
 11,  15,  16,   5,   7,   8,  15,  11,  /*    2 */
 15,   7,  17,  17,  11,  16,  15,   3,  /*    2 */
 11,  18,  16,   6,  19,  19,  19,   3,  /*    2 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*    3 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*    3 */
 20,  20,  20,  20,  20,  20,  20,   7,  /*    3 */
 20,  20,  20,  20,  20,  20,  20,  16,  /*    3 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*    3 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*    3 */
 21,  21,  21,  21,  21,  21,  21,   7,  /*    3 */
 21,  21,  21,  21,  21,  21,  21,  22,  /*    3 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */
 25,  26,  23,  24,  23,  24,  23,  24,  /*    4 */
 16,  23,  24,  23,  24,  23,  24,  23,  /*    4 */
 24,  23,  24,  23,  24,  23,  24,  23,  /*    5 */
 24,  16,  23,  24,  23,  24,  23,  24,  /*    5 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */
 27,  23,  24,  23,  24,  23,  24,  28,  /*    5 */
 16,  29,  23,  24,  23,  24,  30,  23,  /*    6 */
 24,  31,  31,  23,  24,  16,  32,  32,  /*    6 */
 33,  23,  24,  31,  34,  16,  35,  36,  /*    6 */
 23,  24,  16,  16,  35,  37,  16,  38,  /*    6 */
 23,  24,  23,  24,  23,  24,  38,  23,  /*    6 */
 24,  39,  40,  16,  23,  24,  39,  23,  /*    6 */
 24,  41,  41,  23,  24,  23,  24,  42,  /*    6 */
 23,  24,  16,  40,  23,  24,  40,  40,  /*    6 */
 40,  40,  40,  40,  43,  44,  45,  43,  /*    7 */
 44,  45,  43,  44,  45,  23,  24,  23,  /*    7 */
 24,  23,  24,  23,  24,  23,  24,  23,  /*    7 */
 24,  23,  24,  23,  24,  16,  23,  24,  /*    7 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    7 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    7 */
 16,  43,  44,  45,  23,  24,  46,  46,  /*    7 */
 46,  46,  23,  24,  23,  24,  23,  24,  /*    7 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    8 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    8 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*    8 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    9 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*    9 */
 16,  16,  16,  47,  48,  16,  49,  49,  /*    9 */
 50,  50,  16,  51,  16,  16,  16,  16,  /*    9 */
 49,  16,  16,  52,  16,  16,  16,  16,  /*    9 */
 53,  54,  16,  16,  16,  16,  16,  54,  /*    9 */
 16,  16,  55,  16,  16,  16,  16,  16,  /*    9 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*    9 */
 16,  16,  16,  56,  16,  16,  16,  16,  /*   10 */
 56,  16,  57,  57,  16,  16,  16,  16,  /*   10 */
 16,  16,  58,  16,  16,  16,  16,  16,  /*   10 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*   10 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*   10 */
 16,  46,  46,  46,  46,  46,  46,  46,  /*   10 */
 59,  59,  59,  59,  59,  59,  59,  59,  /*   10 */
 59,  11,  11,  59,  59,  59,  59,  59,  /*   10 */
 59,  59,  11,  11,  11,  11,  11,  11,  /*   11 */
 11,  11,  11,  11,  11,  11,  11,  11,  /*   11 */
 59,  59,  11,  11,  11,  11,  11,  11,  /*   11 */
 11,  11,  11,  11,  11,  11,  11,  46,  /*   11 */
 59,  59,  59,  59,  59,  11,  11,  11,  /*   11 */
 11,  11,  46,  46,  46,  46,  46,  46,  /*   11 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   11 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   11 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */
 60,  60,  60,  60,  60,  60,  46,  46,  /*   13 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */
 60,  60,  46,  46,  46,  46,  46,  46,  /*   13 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */
 46,  46,  46,  46,   3,   3,  46,  46,  /*   13 */
 46,  46,  59,  46,  46,  46,   3,  46,  /*   13 */
 46,  46,  46,  46,  11,  11,  61,   3,  /*   14 */
 62,  62,  62,  46,  63,  46,  64,  64,  /*   14 */
 16,  20,  20,  20,  20,  20,  20,  20,  /*   14 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*   14 */
 20,  20,  46,  20,  20,  20,  20,  20,  /*   14 */
 20,  20,  20,  20,  65,  66,  66,  66,  /*   14 */
 16,  21,  21,  21,  21,  21,  21,  21,  /*   14 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*   14 */
 21,  21,  16,  21,  21,  21,  21,  21,  /*   15 */
 21,  21,  21,  21,  67,  68,  68,  46,  /*   15 */
 69,  70,  38,  38,  38,  71,  72,  46,  /*   15 */
 46,  46,  38,  46,  38,  46,  38,  46,  /*   15 */
 38,  46,  23,  24,  23,  24,  23,  24,  /*   15 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   15 */
 73,  74,  16,  40,  46,  46,  46,  46,  /*   15 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   15 */
 46,  75,  75,  75,  75,  75,  75,  75,  /*   16 */
 75,  75,  75,  75,  75,  46,  75,  75,  /*   16 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */
 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*   16 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*   16 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*   17 */
 21,  21,  21,  21,  21,  21,  21,  21,  /*   17 */
 46,  74,  74,  74,  74,  74,  74,  74,  /*   17 */
 74,  74,  74,  74,  74,  46,  74,  74,  /*   17 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */
 23,  24,  15,  60,  60,  60,  60,  46,  /*   18 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   18 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */
 40,  23,  24,  23,  24,  46,  46,  23,  /*   19 */
 24,  46,  46,  23,  24,  46,  46,  46,  /*   19 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   19 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   19 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   19 */
 23,  24,  23,  24,  46,  46,  23,  24,  /*   19 */
 23,  24,  23,  24,  23,  24,  46,  46,  /*   19 */
 23,  24,  46,  46,  46,  46,  46,  46,  /*   19 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */
 46,  76,  76,  76,  76,  76,  76,  76,  /*   20 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   20 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   21 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   21 */
 76,  76,  76,  76,  76,  76,  76,  46,  /*   21 */
 46,  59,   3,   3,   3,   3,   3,   3,  /*   21 */
 46,  77,  77,  77,  77,  77,  77,  77,  /*   21 */
 77,  77,  77,  77,  77,  77,  77,  77,  /*   21 */
 77,  77,  77,  77,  77,  77,  77,  77,  /*   21 */
 77,  77,  77,  77,  77,  77,  77,  77,  /*   21 */
 77,  77,  77,  77,  77,  77,  77,  16,  /*   22 */
 46,   3,  46,  46,  46,  46,  46,  46,  /*   22 */
 46,  60,  60,  60,  60,  60,  60,  60,  /*   22 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   22 */
 60,  60,  46,  60,  60,  60,  60,  60,  /*   22 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   22 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   22 */
 60,  60,  46,  60,  60,  60,   3,  60,  /*   22 */
  3,  60,  60,   3,  60,  46,  46,  46,  /*   23 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   23 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   23 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   23 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   23 */
 40,  40,  40,  46,  46,  46,  46,  46,  /*   23 */
 40,  40,  40,   3,   3,  46,  46,  46,  /*   23 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   23 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   24 */
 46,  46,  46,  46,   3,  46,  46,  46,  /*   24 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   24 */
 46,  46,  46,   3,  46,  46,  46,   3,  /*   24 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   24 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   24 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   24 */
 40,  40,  40,  46,  46,  46,  46,  46,  /*   24 */
 59,  40,  40,  40,  40,  40,  40,  40,  /*   25 */
 40,  40,  40,  60,  60,  60,  60,  60,  /*   25 */
 60,  60,  60,  46,  46,  46,  46,  46,  /*   25 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   25 */
 78,  78,  78,  78,  78,  78,  78,  78,  /*   25 */
 78,  78,   3,   3,   3,   3,  46,  46,  /*   25 */
 60,  40,  40,  40,  40,  40,  40,  40,  /*   25 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   25 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */
 46,  46,  40,  40,  40,  40,  40,  46,  /*   26 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   27 */
 40,  40,  40,  40,  40,  40,  40,  46,  /*   27 */
 40,  40,  40,  40,   3,  40,  60,  60,  /*   27 */
 60,  60,  60,  60,  60,  79,  79,  60,  /*   27 */
 60,  60,  60,  60,  60,  59,  59,  60,  /*   27 */
 60,  15,  60,  60,  60,  60,  46,  46,  /*   27 */
  9,   9,   9,   9,   9,   9,   9,   9,  /*   27 */
  9,   9,  46,  46,  46,  46,  46,  46,  /*   27 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */
 46,  60,  60,  80,  46,  40,  40,  40,  /*   29 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */
 40,  40,  46,  46,  60,  40,  80,  80,  /*   29 */
 80,  60,  60,  60,  60,  60,  60,  60,  /*   30 */
 60,  80,  80,  80,  80,  60,  46,  46,  /*   30 */
 15,  60,  60,  60,  60,  46,  46,  46,  /*   30 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   30 */
 40,  40,  60,  60,   3,   3,  81,  81,  /*   30 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   30 */
  3,  46,  46,  46,  46,  46,  46,  46,  /*   30 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   30 */
 46,  60,  80,  80,  46,  40,  40,  40,  /*   31 */
 40,  40,  40,  40,  40,  46,  46,  40,  /*   31 */
 40,  46,  46,  40,  40,  40,  40,  40,  /*   31 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   31 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   31 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   31 */
 40,  46,  40,  46,  46,  46,  40,  40,  /*   31 */
 40,  40,  46,  46,  60,  46,  80,  80,  /*   31 */
 80,  60,  60,  60,  60,  46,  46,  80,  /*   32 */
 80,  46,  46,  80,  80,  60,  46,  46,  /*   32 */
 46,  46,  46,  46,  46,  46,  46,  80,  /*   32 */
 46,  46,  46,  46,  40,  40,  46,  40,  /*   32 */
 40,  40,  60,  60,  46,  46,  81,  81,  /*   32 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   32 */
 40,  40,   4,   4,  82,  82,  82,  82,  /*   32 */
 19,  83,  15,  46,  46,  46,  46,  46,  /*   32 */
 46,  46,  60,  46,  46,  40,  40,  40,  /*   33 */
 40,  40,  40,  46,  46,  46,  46,  40,  /*   33 */
 40,  46,  46,  40,  40,  40,  40,  40,  /*   33 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   33 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   33 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   33 */
 40,  46,  40,  40,  46,  40,  40,  46,  /*   33 */
 40,  40,  46,  46,  60,  46,  80,  80,  /*   33 */
 80,  60,  60,  46,  46,  46,  46,  60,  /*   34 */
 60,  46,  46,  60,  60,  60,  46,  46,  /*   34 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   34 */
 46,  40,  40,  40,  40,  46,  40,  46,  /*   34 */
 46,  46,  46,  46,  46,  46,  81,  81,  /*   34 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   34 */
 60,  60,  40,  40,  40,  46,  46,  46,  /*   34 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   34 */
 46,  60,  60,  80,  46,  40,  40,  40,  /*   35 */
 40,  40,  40,  40,  46,  40,  46,  40,  /*   35 */
 40,  40,  46,  40,  40,  40,  40,  40,  /*   35 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   35 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   35 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   35 */
 40,  46,  40,  40,  46,  40,  40,  40,  /*   35 */
 40,  40,  46,  46,  60,  40,  80,  80,  /*   35 */
 80,  60,  60,  60,  60,  60,  46,  60,  /*   36 */
 60,  80,  46,  80,  80,  60,  46,  46,  /*   36 */
 15,  46,  46,  46,  46,  46,  46,  46,  /*   36 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   36 */
 40,  46,  46,  46,  46,  46,  81,  81,  /*   36 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   36 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   36 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   36 */
 46,  60,  80,  80,  46,  40,  40,  40,  /*   37 */
 40,  40,  40,  40,  40,  46,  46,  40,  /*   37 */
 40,  46,  46,  40,  40,  40,  40,  40,  /*   37 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   37 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   37 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   37 */
 40,  46,  40,  40,  46,  46,  40,  40,  /*   37 */
 40,  40,  46,  46,  60,  40,  80,  60,  /*   37 */
 80,  60,  60,  60,  46,  46,  46,  80,  /*   38 */
 80,  46,  46,  80,  80,  60,  46,  46,  /*   38 */
 46,  46,  46,  46,  46,  46,  60,  80,  /*   38 */
 46,  46,  46,  46,  40,  40,  46,  40,  /*   38 */
 40,  40,  46,  46,  46,  46,  81,  81,  /*   38 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   38 */
 15,  46,  46,  46,  46,  46,  46,  46,  /*   38 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   38 */
 46,  46,  60,  80,  46,  40,  40,  40,  /*   39 */
 40,  40,  40,  46,  46,  46,  40,  40,  /*   39 */
 40,  46,  40,  40,  40,  40,  46,  46,  /*   39 */
 46,  40,  40,  46,  40,  46,  40,  40,  /*   39 */
 46,  46,  46,  40,  40,  46,  46,  46,  /*   39 */
 40,  40,  40,  46,  46,  46,  40,  40,  /*   39 */
 40,  40,  40,  40,  40,  40,  46,  40,  /*   39 */
 40,  40,  46,  46,  46,  46,  80,  80,  /*   39 */
 60,  80,  80,  46,  46,  46,  80,  80,  /*   40 */
 80,  46,  80,  80,  80,  60,  46,  46,  /*   40 */
 46,  46,  46,  46,  46,  46,  46,  80,  /*   40 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   40 */
 46,  46,  46,  46,  46,  46,  46,  81,  /*   40 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   40 */
 84,  19,  19,  46,  46,  46,  46,  46,  /*   40 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   40 */
 46,  80,  80,  80,  46,  40,  40,  40,  /*   41 */
 40,  40,  40,  40,  40,  46,  40,  40,  /*   41 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   41 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   41 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   41 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   41 */
 40,  40,  40,  40,  46,  40,  40,  40,  /*   41 */
 40,  40,  46,  46,  46,  46,  60,  60,  /*   41 */
 60,  80,  80,  80,  80,  46,  60,  60,  /*   42 */
 60,  46,  60,  60,  60,  60,  46,  46,  /*   42 */
 46,  46,  46,  46,  46,  60,  60,  46,  /*   42 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   42 */
 40,  40,  46,  46,  46,  46,  81,  81,  /*   42 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   42 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   42 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   42 */
 46,  46,  80,  80,  46,  40,  40,  40,  /*   43 */
 40,  40,  40,  40,  40,  46,  40,  40,  /*   43 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   43 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   43 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   43 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   43 */
 40,  40,  40,  40,  46,  40,  40,  40,  /*   43 */
 40,  40,  46,  46,  46,  46,  80,  60,  /*   43 */
 80,  80,  80,  80,  80,  46,  60,  80,  /*   44 */
 80,  46,  80,  80,  60,  60,  46,  46,  /*   44 */
 46,  46,  46,  46,  46,  80,  80,  46,  /*   44 */
 46,  46,  46,  46,  46,  46,  40,  46,  /*   44 */
 40,  40,  46,  46,  46,  46,  81,  81,  /*   44 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   44 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   44 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   44 */
 46,  46,  80,  80,  46,  40,  40,  40,  /*   45 */
 40,  40,  40,  40,  40,  46,  40,  40,  /*   45 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   45 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   45 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   45 */
 40,  46,  40,  40,  40,  40,  40,  40,  /*   45 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   45 */
 40,  40,  46,  46,  46,  46,  80,  80,  /*   45 */
 80,  60,  60,  60,  46,  46,  80,  80,  /*   46 */
 80,  46,  80,  80,  80,  60,  46,  46,  /*   46 */
 46,  46,  46,  46,  46,  46,  46,  80,  /*   46 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   46 */
 40,  40,  46,  46,  46,  46,  81,  81,  /*   46 */
 81,  81,  81,  81,  81,  81,  81,  81,  /*   46 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   46 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   46 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   47 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */
 40,  40,  40,  40,  40,  40,  40,   3,  /*   47 */
 40,  60,  40,  40,  60,  60,  60,  60,  /*   47 */
 60,  60,  60,  46,  46,  46,  46,   4,  /*   47 */
 40,  40,  40,  40,  40,  40,  59,  60,  /*   48 */
 60,  60,  60,  60,  60,  60,  60,  15,  /*   48 */
  9,   9,   9,   9,   9,   9,   9,   9,  /*   48 */
  9,   9,   3,   3,  46,  46,  46,  46,  /*   48 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */
 46,  40,  40,  46,  40,  46,  46,  40,  /*   49 */
 40,  46,  40,  46,  46,  40,  46,  46,  /*   49 */
 46,  46,  46,  46,  40,  40,  40,  40,  /*   49 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   49 */
 46,  40,  40,  40,  46,  40,  46,  40,  /*   49 */
 46,  46,  40,  40,  46,  40,  40,   3,  /*   49 */
 40,  60,  40,  40,  60,  60,  60,  60,  /*   49 */
 60,  60,  46,  60,  60,  40,  46,  46,  /*   49 */
 40,  40,  40,  40,  40,  46,  59,  46,  /*   50 */
 60,  60,  60,  60,  60,  60,  46,  46,  /*   50 */
  9,   9,   9,   9,   9,   9,   9,   9,  /*   50 */
  9,   9,  46,  46,  40,  40,  46,  46,  /*   50 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */
 15,  15,  15,  15,   3,   3,   3,   3,  /*   51 */
  3,   3,   3,   3,   3,   3,   3,   3,  /*   51 */
  3,   3,   3,  15,  15,  15,  15,  15,  /*   51 */
 60,  60,  15,  15,  15,  15,  15,  15,  /*   51 */
 78,  78,  78,  78,  78,  78,  78,  78,  /*   51 */
 78,  78,  85,  85,  85,  85,  85,  85,  /*   51 */
 85,  85,  85,  85,  15,  60,  15,  60,  /*   51 */
 15,  60,   5,   6,   5,   6,  80,  80,  /*   51 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   52 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */
 40,  40,  46,  46,  46,  46,  46,  46,  /*   52 */
 46,  60,  60,  60,  60,  60,  60,  60,  /*   52 */
 60,  60,  60,  60,  60,  60,  60,  80,  /*   52 */
 60,  60,  60,  60,  60,   3,  60,  60,  /*   53 */
 60,  60,  60,  60,  46,  46,  46,  46,  /*   53 */
 60,  60,  60,  60,  60,  60,  46,  60,  /*   53 */
 46,  60,  60,  60,  60,  60,  60,  60,  /*   53 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   53 */
 60,  60,  60,  60,  60,  60,  46,  46,  /*   53 */
 46,  60,  60,  60,  60,  60,  60,  60,  /*   53 */
 46,  60,  46,  46,  46,  46,  46,  46,  /*   53 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */
 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */
 76,  76,  76,  76,  76,  76,  46,  46,  /*   55 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   55 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */
 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */
 16,  16,  16,  16,  16,  16,  16,  46,  /*   55 */
 46,  46,  46,   3,  46,  46,  46,  46,  /*   55 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  46,  46,  46,  46,  46,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  46,  46,  46,  46,  46,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */
 40,  40,  46,  46,  46,  46,  46,  46,  /*   59 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */
 23,  24,  23,  24,  23,  24,  16,  16,  /*   61 */
 16,  16,  16,  16,  46,  46,  46,  46,  /*   61 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */
 23,  24,  46,  46,  46,  46,  46,  46,  /*   62 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   63 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   63 */
 86,  86,  86,  86,  86,  86,  46,  46,  /*   63 */
 87,  87,  87,  87,  87,  87,  46,  46,  /*   63 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   63 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   63 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   63 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   63 */
 86,  86,  86,  86,  86,  86,  46,  46,  /*   64 */
 87,  87,  87,  87,  87,  87,  46,  46,  /*   64 */
 16,  86,  16,  86,  16,  86,  16,  86,  /*   64 */
 46,  87,  46,  87,  46,  87,  46,  87,  /*   64 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   64 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   64 */
 88,  88,  89,  89,  89,  89,  90,  90,  /*   64 */
 91,  91,  92,  92,  93,  93,  46,  46,  /*   64 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   65 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   65 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   65 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   65 */
 86,  86,  86,  86,  86,  86,  86,  86,  /*   65 */
 87,  87,  87,  87,  87,  87,  87,  87,  /*   65 */
 86,  86,  16,  94,  16,  46,  16,  16,  /*   65 */
 87,  87,  95,  95,  96,  11,  38,  11,  /*   65 */
 11,  11,  16,  94,  16,  46,  16,  16,  /*   66 */
 97,  97,  97,  97,  96,  11,  11,  11,  /*   66 */
 86,  86,  16,  16,  46,  46,  16,  16,  /*   66 */
 87,  87,  98,  98,  46,  11,  11,  11,  /*   66 */
 86,  86,  16,  16,  16,  99,  16,  16,  /*   66 */
 87,  87, 100, 100, 101,  11,  11,  11,  /*   66 */
 46,  46,  16,  94,  16,  46,  16,  16,  /*   66 */
102, 102, 103, 103,  96,  11,  11,  46,  /*   66 */
  2,   2,   2,   2,   2,   2,   2,   2,  /*   67 */
  2,   2,   2,   2, 104, 104, 104, 104,  /*   67 */
  8,   8,   8,   8,   8,   8,   3,   3,  /*   67 */
  5,   6,   5,   5,   5,   6,   5,   5,  /*   67 */
  3,   3,   3,   3,   3,   3,   3,   3,  /*   67 */
105, 106, 104, 104, 104, 104, 104,  46,  /*   67 */
  3,   3,   3,   3,   3,   3,   3,   3,  /*   67 */
  3,   5,   6,   3,   3,   3,   3,  12,  /*   67 */
 12,   3,   3,   3,   7,   5,   6,  46,  /*   68 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */
 46,  46, 104, 104, 104, 104, 104, 104,  /*   68 */
 17,  46,  46,  46,  17,  17,  17,  17,  /*   68 */
 17,  17,   7,   7,   7,   5,   6,  16,  /*   68 */
107, 107, 107, 107, 107, 107, 107, 107,  /*   69 */
107, 107,   7,   7,   7,   5,   6,  46,  /*   69 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */
  4,   4,   4,   4,   4,   4,   4,   4,  /*   69 */
  4,   4,   4,   4,  46,  46,  46,  46,  /*   69 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */
 60,  60,  60,  60,  60,  60,  60,  60,  /*   70 */
 60,  60,  60,  60,  60,  79,  79,  79,  /*   70 */
 79,  60,  46,  46,  46,  46,  46,  46,  /*   70 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */
 15,  15,  38,  15,  15,  15,  15,  38,  /*   71 */
 15,  15,  16,  38,  38,  38,  16,  16,  /*   71 */
 38,  38,  38,  16,  15,  38,  15,  15,  /*   71 */
 38,  38,  38,  38,  38,  38,  15,  15,  /*   71 */
 15,  15,  15,  15,  38,  15,  38,  15,  /*   71 */
 38,  15,  38,  38,  38,  38,  16,  16,  /*   71 */
 38,  38,  15,  38,  16,  40,  40,  40,  /*   71 */
 40,  46,  46,  46,  46,  46,  46,  46,  /*   71 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   72 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   72 */
 46,  46,  46,  19,  19,  19,  19,  19,  /*   72 */
 19,  19,  19,  19,  19,  19,  19, 108,  /*   72 */
109, 109, 109, 109, 109, 109, 109, 109,  /*   72 */
109, 109, 109, 109, 110, 110, 110, 110,  /*   72 */
111, 111, 111, 111, 111, 111, 111, 111,  /*   72 */
111, 111, 111, 111, 112, 112, 112, 112,  /*   72 */
113, 113, 113,  46,  46,  46,  46,  46,  /*   73 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   73 */
  7,   7,   7,   7,   7,  15,  15,  15,  /*   73 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */
 15,  15,   7,  15,   7,  15,  15,  15,  /*   74 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */
 15,  15,  15,  46,  46,  46,  46,  46,  /*   74 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   74 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   74 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */
  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */
  7,   7,  46,  46,  46,  46,  46,  46,  /*   76 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   76 */
 15,  46,  15,  15,  15,  15,  15,  15,  /*   77 */
  7,   7,   7,   7,  15,  15,  15,  15,  /*   77 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */
  7,   7,  15,  15,  15,  15,  15,  15,  /*   77 */
 15,   5,   6,  15,  15,  15,  15,  15,  /*   77 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */
 15,  15,  15,  46,  46,  46,  46,  46,  /*   78 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */
 15,  15,  15,  15,  15,  46,  46,  46,  /*   79 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   79 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   79 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   79 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   80 */
 15,  15,  15,  46,  46,  46,  46,  46,  /*   80 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   80 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   80 */
114, 114, 114, 114, 114, 114, 114, 114,  /*   80 */
114, 114, 114, 114, 114, 114, 114, 114,  /*   80 */
114, 114, 114, 114,  82,  82,  82,  82,  /*   80 */
 82,  82,  82,  82,  82,  82,  82,  82,  /*   80 */
 82,  82,  82,  82,  82,  82,  82,  82,  /*   81 */
115, 115, 115, 115, 115, 115, 115, 115,  /*   81 */
115, 115, 115, 115, 115, 115, 115, 115,  /*   81 */
115, 115, 115, 115,  15,  15,  15,  15,  /*   81 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   81 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   81 */
 15,  15,  15,  15,  15,  15, 116, 116,  /*   81 */
116, 116, 116, 116, 116, 116, 116, 116,  /*   81 */
116, 116, 116, 116, 116, 116, 116, 116,  /*   82 */
116, 116, 116, 116, 116, 116, 116, 116,  /*   82 */
117, 117, 117, 117, 117, 117, 117, 117,  /*   82 */
117, 117, 117, 117, 117, 117, 117, 117,  /*   82 */
117, 117, 117, 117, 117, 117, 117, 117,  /*   82 */
117, 117, 118,  46,  46,  46,  46,  46,  /*   82 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   82 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   82 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */
 15,  15,  15,  15,  15,  15,  46,  46,  /*   84 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   84 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   85 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   85 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */
 15,  15,  15,  15,  46,  46,  46,  46,  /*   86 */
 46,  46,  15,  15,  15,  15,  15,  15,  /*   86 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */
 46,  15,  15,  15,  15,  46,  15,  15,  /*   87 */
 15,  15,  46,  46,  15,  15,  15,  15,  /*   87 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */
 46,  15,  15,  15,  15,  15,  15,  15,  /*   87 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   88 */
 15,  15,  15,  15,  46,  15,  46,  15,  /*   88 */
 15,  15,  15,  46,  46,  46,  15,  46,  /*   88 */
 15,  15,  15,  15,  15,  15,  15,  46,  /*   88 */
 46,  15,  15,  15,  15,  15,  15,  15,  /*   88 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   88 */
 46,  46,  46,  46,  46,  46, 119, 119,  /*   88 */
119, 119, 119, 119, 119, 119, 119, 119,  /*   88 */
114, 114, 114, 114, 114, 114, 114, 114,  /*   89 */
114, 114,  83,  83,  83,  83,  83,  83,  /*   89 */
 83,  83,  83,  83,  15,  46,  46,  46,  /*   89 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   89 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   89 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   89 */
 46,  15,  15,  15,  15,  15,  15,  15,  /*   89 */
 15,  15,  15,  15,  15,  15,  15,  46,  /*   89 */
  2,   3,   3,   3,  15,  59,   3, 120,  /*   90 */
  5,   6,   5,   6,   5,   6,   5,   6,  /*   90 */
  5,   6,  15,  15,   5,   6,   5,   6,  /*   90 */
  5,   6,   5,   6,   8,   5,   6,   5,  /*   90 */
 15, 121, 121, 121, 121, 121, 121, 121,  /*   90 */
121, 121,  60,  60,  60,  60,  60,  60,  /*   90 */
  8,  59,  59,  59,  59,  59,  15,  15,  /*   90 */
 46,  46,  46,  46,  46,  46,  46,  15,  /*   90 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */
 40,  40,  40,  40,  40,  46,  46,  46,  /*   92 */
 46,  60,  60,  59,  59,  59,  59,  46,  /*   92 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   92 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */
 40,  40,  40,   3,  59,  59,  59,  46,  /*   93 */
 46,  46,  46,  46,  46,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  46,  46,  46,  /*   94 */
 46,  40,  40,  40,  40,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*   95 */
 40,  40,  40,  40,  40,  40,  40,  46,  /*   95 */
 15,  15,  85,  85,  85,  85,  15,  15,  /*   95 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   95 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */
 15,  15,  15,  15,  15,  46,  46,  46,  /*   96 */
 85,  85,  85,  85,  85,  85,  85,  85,  /*   96 */
 85,  85,  15,  15,  15,  15,  15,  15,  /*   96 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */
 15,  15,  15,  15,  46,  46,  46,  46,  /*   97 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   97 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   97 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   97 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   97 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   97 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   97 */
 15,  15,  15,  15,  46,  46,  46,  15,  /*   97 */
114, 114, 114, 114, 114, 114, 114, 114,  /*   98 */
114, 114,  15,  15,  15,  15,  15,  15,  /*   98 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */
 15,  46,  46,  46,  46,  46,  46,  46,  /*   98 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*   98 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */
 15,  15,  15,  15,  46,  46,  46,  46,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  46,  /*   99 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  46,  /*  100 */
 46,  46,  46,  15,  15,  15,  15,  15,  /*  100 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */
 15,  15,  15,  15,  15,  15,  46,  46,  /*  101 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */
 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */
 15,  15,  15,  15,  15,  15,  15,  46,  /*  101 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */
 40,  40,  40,  40,  40,  40,  46,  46,  /*  102 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  102 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  102 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  102 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */
 40,  40,  40,  40,  46,  46,  46,  46,  /*  103 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  103 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  103 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  103 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */
 40,  40,  40,  40,  40,  40,  46,  46,  /*  106 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  106 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  106 */
 16,  16,  16,  16,  16,  16,  16,  46,  /*  107 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  107 */
 46,  46,  46,  16,  16,  16,  16,  16,  /*  107 */
 46,  46,  46,  46,  46,  46,  60,  40,  /*  107 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  107 */
 40,   7,  40,  40,  40,  40,  40,  40,  /*  107 */
 40,  40,  40,  40,  40,  40,  40,  46,  /*  107 */
 40,  40,  40,  40,  40,  46,  40,  46,  /*  107 */
 40,  40,  46,  40,  40,  46,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */
 40,  40,  46,  46,  46,  46,  46,  46,  /*  109 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  109 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  110 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  110 */
 46,  46,  46,  40,  40,  40,  40,  40,  /*  110 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */
 40,  40,  40,  40,  40,  40,   5,   6,  /*  111 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  112 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 46,  46,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  114 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  114 */
 40,  40,  40,  40,  46,  46,  46,  46,  /*  114 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */
 60,  60,  60,  60,  46,  46,  46,  46,  /*  115 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */
  3,   8,   8,  12,  12,   5,   6,   5,  /*  115 */
  6,   5,   6,   5,   6,   5,   6,   5,  /*  115 */
  6,   5,   6,   5,   6,  46,  46,  46,  /*  116 */
 46,   3,   3,   3,   3,  12,  12,  12,  /*  116 */
  3,   3,   3,  46,   3,   3,   3,   3,  /*  116 */
  8,   5,   6,   5,   6,   5,   6,   3,  /*  116 */
  3,   3,   7,   8,   7,   7,   7,  46,  /*  116 */
  3,   4,   3,   3,  46,  46,  46,  46,  /*  116 */
 40,  40,  40,  46,  40,  46,  40,  40,  /*  116 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  116 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */
 40,  40,  40,  40,  40,  46,  46, 104,  /*  117 */
 46,   3,   3,   3,   4,   3,   3,   3,  /*  118 */
  5,   6,   3,   7,   3,   8,   3,   3,  /*  118 */
  9,   9,   9,   9,   9,   9,   9,   9,  /*  118 */
  9,   9,   3,   3,   7,   7,   7,   3,  /*  118 */
  3,  10,  10,  10,  10,  10,  10,  10,  /*  118 */
 10,  10,  10,  10,  10,  10,  10,  10,  /*  118 */
 10,  10,  10,  10,  10,  10,  10,  10,  /*  118 */
 10,  10,  10,   5,   3,   6,  11,  12,  /*  118 */
 11,  13,  13,  13,  13,  13,  13,  13,  /*  119 */
 13,  13,  13,  13,  13,  13,  13,  13,  /*  119 */
 13,  13,  13,  13,  13,  13,  13,  13,  /*  119 */
 13,  13,  13,   5,   7,   6,   7,  46,  /*  119 */
 46,   3,   5,   6,   3,   3,  40,  40,  /*  119 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  119 */
 59,  40,  40,  40,  40,  40,  40,  40,  /*  119 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  119 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */
 40,  40,  40,  40,  40,  40,  59,  59,  /*  120 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */
 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */
 40,  40,  40,  40,  40,  40,  40,  46,  /*  120 */
 46,  46,  40,  40,  40,  40,  40,  40,  /*  121 */
 46,  46,  40,  40,  40,  40,  40,  40,  /*  121 */
 46,  46,  40,  40,  40,  40,  40,  40,  /*  121 */
 46,  46,  40,  40,  40,  46,  46,  46,  /*  121 */
  4,   4,   7,  11,  15,   4,   4,  46,  /*  121 */
  7,   7,   7,   7,   7,  15,  15,  46,  /*  121 */
 46,  46,  46,  46,  46,  46,  46,  46,  /*  121 */
 46,  46,  46,  46,  46,  15,  46,  46   /*  121 */
};

/* The A table has 124 entries for a total of 496 bytes. */

const uint32 js_A[] = {
0x0001000F,  /*    0   Cc, ignorable */
0x0004000F,  /*    1   Cc, whitespace */
0x0004000C,  /*    2   Zs, whitespace */
0x00000018,  /*    3   Po */
0x0006001A,  /*    4   Sc, currency */
0x00000015,  /*    5   Ps */
0x00000016,  /*    6   Pe */
0x00000019,  /*    7   Sm */
0x00000014,  /*    8   Pd */
0x00036009,  /*    9   Nd, identifier part, decimal 16 */
0x0827FE01,  /*   10   Lu, hasLower (add 32), identifier start, supradecimal 31 */
0x0000001B,  /*   11   Sk */
0x00050017,  /*   12   Pc, underscore */
0x0817FE02,  /*   13   Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */
0x0000000C,  /*   14   Zs */
0x0000001C,  /*   15   So */
0x00070002,  /*   16   Ll, identifier start */
0x0000600B,  /*   17   No, decimal 16 */
0x0000500B,  /*   18   No, decimal 8 */
0x0000800B,  /*   19   No, strange */
0x08270001,  /*   20   Lu, hasLower (add 32), identifier start */
0x08170002,  /*   21   Ll, hasUpper (subtract 32), identifier start */
0xE1D70002,  /*   22   Ll, hasUpper (subtract -121), identifier start */
0x00670001,  /*   23   Lu, hasLower (add 1), identifier start */
0x00570002,  /*   24   Ll, hasUpper (subtract 1), identifier start */
0xCE670001,  /*   25   Lu, hasLower (add -199), identifier start */
0x3A170002,  /*   26   Ll, hasUpper (subtract 232), identifier start */
0xE1E70001,  /*   27   Lu, hasLower (add -121), identifier start */
0x4B170002,  /*   28   Ll, hasUpper (subtract 300), identifier start */
0x34A70001,  /*   29   Lu, hasLower (add 210), identifier start */
0x33A70001,  /*   30   Lu, hasLower (add 206), identifier start */
0x33670001,  /*   31   Lu, hasLower (add 205), identifier start */
0x32A70001,  /*   32   Lu, hasLower (add 202), identifier start */
0x32E70001,  /*   33   Lu, hasLower (add 203), identifier start */
0x33E70001,  /*   34   Lu, hasLower (add 207), identifier start */
0x34E70001,  /*   35   Lu, hasLower (add 211), identifier start */
0x34670001,  /*   36   Lu, hasLower (add 209), identifier start */
0x35670001,  /*   37   Lu, hasLower (add 213), identifier start */
0x00070001,  /*   38   Lu, identifier start */
0x36A70001,  /*   39   Lu, hasLower (add 218), identifier start */
0x00070005,  /*   40   Lo, identifier start */
0x36670001,  /*   41   Lu, hasLower (add 217), identifier start */
0x36E70001,  /*   42   Lu, hasLower (add 219), identifier start */
0x00AF0001,  /*   43   Lu, hasLower (add 2), hasTitle, identifier start */
0x007F0003,  /*   44   Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */
0x009F0002,  /*   45   Ll, hasUpper (subtract 2), hasTitle, identifier start */
0x00000000,  /*   46   unassigned */
0x34970002,  /*   47   Ll, hasUpper (subtract 210), identifier start */
0x33970002,  /*   48   Ll, hasUpper (subtract 206), identifier start */
0x33570002,  /*   49   Ll, hasUpper (subtract 205), identifier start */
0x32970002,  /*   50   Ll, hasUpper (subtract 202), identifier start */
0x32D70002,  /*   51   Ll, hasUpper (subtract 203), identifier start */
0x33D70002,  /*   52   Ll, hasUpper (subtract 207), identifier start */
0x34570002,  /*   53   Ll, hasUpper (subtract 209), identifier start */
0x34D70002,  /*   54   Ll, hasUpper (subtract 211), identifier start */
0x35570002,  /*   55   Ll, hasUpper (subtract 213), identifier start */
0x36970002,  /*   56   Ll, hasUpper (subtract 218), identifier start */
0x36570002,  /*   57   Ll, hasUpper (subtract 217), identifier start */
0x36D70002,  /*   58   Ll, hasUpper (subtract 219), identifier start */
0x00070004,  /*   59   Lm, identifier start */
0x00030006,  /*   60   Mn, identifier part */
0x09A70001,  /*   61   Lu, hasLower (add 38), identifier start */
0x09670001,  /*   62   Lu, hasLower (add 37), identifier start */
0x10270001,  /*   63   Lu, hasLower (add 64), identifier start */
0x0FE70001,  /*   64   Lu, hasLower (add 63), identifier start */
0x09970002,  /*   65   Ll, hasUpper (subtract 38), identifier start */
0x09570002,  /*   66   Ll, hasUpper (subtract 37), identifier start */
0x10170002,  /*   67   Ll, hasUpper (subtract 64), identifier start */
0x0FD70002,  /*   68   Ll, hasUpper (subtract 63), identifier start */
0x0F970002,  /*   69   Ll, hasUpper (subtract 62), identifier start */
0x0E570002,  /*   70   Ll, hasUpper (subtract 57), identifier start */
0x0BD70002,  /*   71   Ll, hasUpper (subtract 47), identifier start */
0x0D970002,  /*   72   Ll, hasUpper (subtract 54), identifier start */
0x15970002,  /*   73   Ll, hasUpper (subtract 86), identifier start */
0x14170002,  /*   74   Ll, hasUpper (subtract 80), identifier start */
0x14270001,  /*   75   Lu, hasLower (add 80), identifier start */
0x0C270001,  /*   76   Lu, hasLower (add 48), identifier start */
0x0C170002,  /*   77   Ll, hasUpper (subtract 48), identifier start */
0x00034009,  /*   78   Nd, identifier part, decimal 0 */
0x00000007,  /*   79   Me */
0x00030008,  /*   80   Mc, identifier part */
0x00037409,  /*   81   Nd, identifier part, decimal 26 */
0x00005A0B,  /*   82   No, decimal 13 */
0x00006E0B,  /*   83   No, decimal 23 */
0x0000740B,  /*   84   No, decimal 26 */
0x0000000B,  /*   85   No */
0xFE170002,  /*   86   Ll, hasUpper (subtract -8), identifier start */
0xFE270001,  /*   87   Lu, hasLower (add -8), identifier start */
0xED970002,  /*   88   Ll, hasUpper (subtract -74), identifier start */
0xEA970002,  /*   89   Ll, hasUpper (subtract -86), identifier start */
0xE7170002,  /*   90   Ll, hasUpper (subtract -100), identifier start */
0xE0170002,  /*   91   Ll, hasUpper (subtract -128), identifier start */
0xE4170002,  /*   92   Ll, hasUpper (subtract -112), identifier start */
0xE0970002,  /*   93   Ll, hasUpper (subtract -126), identifier start */
0xFDD70002,  /*   94   Ll, hasUpper (subtract -9), identifier start */
0xEDA70001,  /*   95   Lu, hasLower (add -74), identifier start */
0xFDE70001,  /*   96   Lu, hasLower (add -9), identifier start */
0xEAA70001,  /*   97   Lu, hasLower (add -86), identifier start */
0xE7270001,  /*   98   Lu, hasLower (add -100), identifier start */
0xFE570002,  /*   99   Ll, hasUpper (subtract -7), identifier start */
0xE4270001,  /*  100   Lu, hasLower (add -112), identifier start */
0xFE670001,  /*  101   Lu, hasLower (add -7), identifier start */
0xE0270001,  /*  102   Lu, hasLower (add -128), identifier start */
0xE0A70001,  /*  103   Lu, hasLower (add -126), identifier start */
0x00010010,  /*  104   Cf, ignorable */
0x0004000D,  /*  105   Zl, whitespace */
0x0004000E,  /*  106   Zp, whitespace */
0x0000400B,  /*  107   No, decimal 0 */
0x0000440B,  /*  108   No, decimal 2 */
0x0427420A,  /*  109   Nl, hasLower (add 16), identifier start, decimal 1 */
0x0427800A,  /*  110   Nl, hasLower (add 16), identifier start, strange */
0x0417620A,  /*  111   Nl, hasUpper (subtract 16), identifier start, decimal 17 */
0x0417800A,  /*  112   Nl, hasUpper (subtract 16), identifier start, strange */
0x0007800A,  /*  113   Nl, identifier start, strange */
0x0000420B,  /*  114   No, decimal 1 */
0x0000720B,  /*  115   No, decimal 25 */
0x06A0001C,  /*  116   So, hasLower (add 26) */
0x0690001C,  /*  117   So, hasUpper (subtract 26) */
0x00006C0B,  /*  118   No, decimal 22 */
0x0000560B,  /*  119   No, decimal 11 */
0x0007720A,  /*  120   Nl, identifier start, decimal 25 */
0x0007400A,  /*  121   Nl, identifier start, decimal 0 */
0x00000013,  /*  122   Cs */
0x00000012   /*  123   Co */
};

#ifndef __GNUC__
jschar
js_ToUpper(jschar c)
{
    uint32 v = JS_CCODE(c);
    return (v & 0x00100000) ? c - ((int32)v >> 22) : c;
}

jschar
js_ToLower(jschar c)
{
    uint32 v = JS_CCODE(c);
    return (v & 0x00200000) ? c + ((int32)v >> 22) : c;
}
#endif /* !__GNUC__ */

**** End of jsstr.c. ****

**** Start of jsstr.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsstr_h___
#define jsstr_h___
/*
 * JS string type implementation.
 *
 * A JS string is a counted array of unicode characters.  To support handoff
 * of API client memory, the chars are allocated separately from the length,
 * necessitating a pointer after the count, to form a string header.  String
 * headers are GC'ed while string bytes are allocated from the malloc heap.
 *
 * When a string is treated as an object (by following it with . or []), the
 * runtime wraps it with a JSObject whose valueOf method returns the unwrapped
 * string header.
 */
#include <ctype.h>
#include "jspubtd.h"
#include "jsprvtd.h"
#include "jshash.h"

JS_BEGIN_EXTERN_C

struct JSString {
    size_t          length;
    jschar          *chars;
};

struct JSSubString {
    size_t          length;
    const jschar    *chars;
};

extern jschar      js_empty_ucstr[];
extern JSSubString js_EmptySubString;

/* Unicode character attribute lookup tables. */
extern const uint8 js_X[];
extern const uint8 js_Y[];
extern const uint32 js_A[];

/* Enumerated Unicode general category types. */
typedef enum JSCharType {
    JSCT_UNASSIGNED             = 0,
    JSCT_UPPERCASE_LETTER       = 1,
    JSCT_LOWERCASE_LETTER       = 2,
    JSCT_TITLECASE_LETTER       = 3,
    JSCT_MODIFIER_LETTER        = 4,
    JSCT_OTHER_LETTER           = 5,
    JSCT_NON_SPACING_MARK       = 6,
    JSCT_ENCLOSING_MARK         = 7,
    JSCT_COMBINING_SPACING_MARK = 8,
    JSCT_DECIMAL_DIGIT_NUMBER   = 9,
    JSCT_LETTER_NUMBER          = 10,
    JSCT_OTHER_NUMBER           = 11,
    JSCT_SPACE_SEPARATOR        = 12,
    JSCT_LINE_SEPARATOR         = 13,
    JSCT_PARAGRAPH_SEPARATOR    = 14,
    JSCT_CONTROL                = 15,
    JSCT_FORMAT                 = 16,
    JSCT_PRIVATE_USE            = 18,
    JSCT_SURROGATE              = 19,
    JSCT_DASH_PUNCTUATION       = 20,
    JSCT_START_PUNCTUATION      = 21,
    JSCT_END_PUNCTUATION        = 22,
    JSCT_CONNECTOR_PUNCTUATION  = 23,
    JSCT_OTHER_PUNCTUATION      = 24,
    JSCT_MATH_SYMBOL            = 25,
    JSCT_CURRENCY_SYMBOL        = 26,
    JSCT_MODIFIER_SYMBOL        = 27,
    JSCT_OTHER_SYMBOL           = 28
} JSCharType;

/* Character classifying and mapping macros, based on java.lang.Character. */
#define JS_CCODE(c)     (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]])
#define JS_CTYPE(c)     (JS_CCODE(c) & 0x1F)

#define JS_ISALPHA(c)   ((((1 << JSCT_UPPERCASE_LETTER) |                     \
			   (1 << JSCT_LOWERCASE_LETTER) |                     \
			   (1 << JSCT_TITLECASE_LETTER) |                     \
			   (1 << JSCT_MODIFIER_LETTER) |                      \
			   (1 << JSCT_OTHER_LETTER))                          \
			  >> JS_CTYPE(c)) & 1)

#define JS_ISALNUM(c)   ((((1 << JSCT_UPPERCASE_LETTER) |                     \
			   (1 << JSCT_LOWERCASE_LETTER) |                     \
			   (1 << JSCT_TITLECASE_LETTER) |                     \
			   (1 << JSCT_MODIFIER_LETTER) |                      \
			   (1 << JSCT_OTHER_LETTER) |                         \
			   (1 << JSCT_DECIMAL_DIGIT_NUMBER))                  \
			  >> JS_CTYPE(c)) & 1)

#define JS_ISWORD(c)    (JS_ISALNUM(c) || (c) == '_')

/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */
#define JS_ISIDENT(c)   ((c) < 128 && (isalpha(c) || (c) == '_' || (c) == '$'))
#define JS_ISIDENT2(c)  ((c) < 128 && (isalnum(c) || (c) == '_' || (c) == '$'))

#define JS_ISDIGIT(c)   (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER)

/* XXXbe fs, etc. ? */
#define JS_ISSPACE(c)   ((JS_CCODE(c) & 0x00070000) == 0x00040000)
#define JS_ISPRINT(c)   ((c) < 128 && isprint(c))

#define JS_ISUPPER(c)   (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER)
#define JS_ISLOWER(c)   (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER)

#ifdef __GNUC__

#define JS_TOUPPER(c)   ({uint32 _v = JS_CCODE(c);                            \
			  (_v & 0x00100000) ? (c) - ((int32)_v >> 22) : (c);})
#define JS_TOLOWER(c)   ({uint32 _v = JS_CCODE(c);                            \
			  (_v & 0x00200000) ? (c) + ((int32)_v >> 22) : (c);})

#else  /* !__GNUC__ */

#define JS_TOUPPER(c)   js_ToUpper((jschar)c)
#define JS_TOLOWER(c)   js_ToLower((jschar)c)

extern jschar js_ToUpper(jschar c);
extern jschar js_ToLower(jschar c);

#endif /* !__GNUC__ */

#define JS_TOCTRL(c)    ((c) ^ 64)      /* XXX unsafe! requires uppercase c */

/* Shorthands for ASCII (7-bit) decimal and hex conversion. */
#define JS7_ISDEC(c)    ((c) < 128 && isdigit(c))
#define JS7_UNDEC(c)    ((c) - '0')
#define JS7_ISHEX(c)    ((c) < 128 && isxdigit(c))
#define JS7_UNHEX(c)    (uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a')
#define JS7_ISLET(c)    ((c) < 128 && isalpha(c))

/* Initialize truly global state associated with JS strings. */
extern JSBool
js_InitStringGlobals(void);

extern void
js_FreeStringGlobals(void);

/* Initialize the String class, returning its prototype object. */
extern JSObject *
js_InitStringClass(JSContext *cx, JSObject *obj);

/* GC-allocate a string descriptor for the given malloc-allocated chars. */
extern JSString *
js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag);

/* Copy a counted string and GC-allocate a descriptor for it. */
extern JSString *
js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag);

/* Copy a C string and GC-allocate a descriptor for it. */
extern JSString *
js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag);

/* Free the chars held by str when it is finalized by the GC. */
extern void
js_FinalizeString(JSContext *cx, JSString *str);

/* Wrap a string value in a String object. */
extern JSObject *
js_StringToObject(JSContext *cx, JSString *str);

/*
 * Convert a value to a string, returning null after reporting an error,
 * otherwise returning a new string reference.
 */
extern JSString *
js_ValueToString(JSContext *cx, jsval v);

/*
 * Convert a value to its source expression, returning null after reporting
 * an error, otherwise returning a new string reference.
 */
extern JSString *
js_ValueToSource(JSContext *cx, jsval v);

#ifdef HT_ENUMERATE_NEXT	/* XXX don't require jshash.h */
/*
 * Compute a hash function from str.
 */
extern JSHashNumber
js_HashString(const JSString *str);
#endif

/*
 * Return less than, equal to, or greater than zero depending on whether
 * str1 is less than, equal to, or greater than str2.
 */
extern intN
js_CompareStrings(const JSString *str1, const JSString *str2);

/*
 * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen.
 * The patlen argument must be positive and no greater than BMH_PATLEN_MAX.
 * The start argument tells where in text to begin the search.
 *
 * Return the index of pat in text, or -1 if not found.
 */
#define BMH_CHARSET_SIZE 256    /* ISO-Latin-1 */
#define BMH_PATLEN_MAX   255    /* skip table element is uint8 */

#define BMH_BAD_PATTERN  (-2)   /* return value if pat is not ISO-Latin-1 */

extern jsint
js_BoyerMooreHorspool(const jschar *text, jsint textlen,
		      const jschar *pat, jsint patlen,
		      jsint start);

extern size_t
js_strlen(const jschar *s);

extern jschar *
js_strchr(const jschar *s, jschar c);

extern jschar *
js_strncpy(jschar *t, const jschar *s, size_t n);

/*
 * Return s advanced past any Unicode white space characters.
 */
extern const jschar *
js_SkipWhiteSpace(const jschar *s);

/*
 * Inflate bytes to JS chars and vice versa.  Report out of memory via cx
 * and return null on error, otherwise return the jschar or byte vector that
 * was JS_malloc'ed.
 */
extern jschar *
js_InflateString(JSContext *cx, const char *bytes, size_t length);

extern char *
js_DeflateString(JSContext *cx, const jschar *chars, size_t length);

/*
 * Associate bytes with str in the deflated string cache, returning true on
 * successful association, false on out of memory.
 */
extern JSBool
js_SetStringBytes(JSString *str, char *bytes, size_t length);

/*
 * Find or create a deflated string cache entry for str that contains its
 * characters chopped from Unicode code points into bytes.
 */
extern char *
js_GetStringBytes(JSString *str);

JS_END_EXTERN_C

#endif /* jsstr_h___ */

**** End of jsstr.h. ****

**** Start of jstypes.h. ****

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
** File:                jstypes.h
** Description: Definitions of NSPR's basic types
**
** Prototypes and macros used to make up for deficiencies in ANSI environments
** that we have found.
**
** Since we do not wrap <stdlib.h> and all the other standard headers, authors
** of portable code will not know in general that they need these definitions.
** Instead of requiring these authors to find the dependent uses in their code
** and take the following steps only in those C files, we take steps once here
** for all C files.
**/

#ifndef jstypes_h___
#define jstypes_h___

#ifndef _STDDEF_H
	#include <stddef.h>
#endif

/***********************************************************************
** MACROS:      JS_EXTERN_API
**              JS_EXPORT_API
** DESCRIPTION:
**      These are only for externally visible routines and globals.  For
**      internal routines, just use "extern" for type checking and that
**      will not export internal cross-file or forward-declared symbols.
**      Define a macro for declaring procedures return types. We use this to
**      deal with windoze specific type hackery for DLL definitions. Use
**      JS_EXTERN_API when the prototype for the method is declared. Use
**      JS_EXPORT_API for the implementation of the method.
**
** Example:
**   in dowhim.h
**     JS_EXTERN_API( void ) DoWhatIMean( void );
**   in dowhim.c
**     JS_EXPORT_API( void ) DoWhatIMean( void ) { return; }
**
**
***********************************************************************/
#if defined(WIN32) && !defined(JS_NO_DLL_DIRECTIVES)
#define JS_EXTERN_API(__type) extern _declspec(dllexport) __type
#define JS_EXPORT_API(__type) _declspec(dllexport) __type
#define JS_EXTERN_DATA(__type) extern _declspec(dllexport) __type
#define JS_EXPORT_DATA(__type) _declspec(dllexport) __type

#define JS_DLL_CALLBACK
#define JS_STATIC_DLL_CALLBACK(__x) static __x

#elif defined(WIN16) && !defined(JS_NO_DLL_DIRECTIVES)

#ifdef _WINDLL
#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds
#define JS_EXPORT_API(__type) __type _cdecl _export _loadds
#define JS_EXTERN_DATA(__type) extern __type _export
#define JS_EXPORT_DATA(__type) __type _export

#define JS_DLL_CALLBACK             __cdecl __loadds
#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK

#else /* this must be .EXE */
#define JS_EXTERN_API(__type) extern __type _cdecl _export
#define JS_EXPORT_API(__type) __type _cdecl _export
#define JS_EXTERN_DATA(__type) extern __type _export
#define JS_EXPORT_DATA(__type) __type _export

#define JS_DLL_CALLBACK             __cdecl __loadds
#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK
#endif /* _WINDLL */

#elif defined(XP_MAC) && !defined(JS_NO_DLL_DIRECTIVES)
#define JS_EXTERN_API(__type) extern __declspec(export) __type
#define JS_EXPORT_API(__type) __declspec(export) __type
#define JS_EXTERN_DATA(__type) extern __declspec(export) __type
#define JS_EXPORT_DATA(__type) __declspec(export) __type

#define JS_DLL_CALLBACK
#define JS_STATIC_DLL_CALLBACK(__x) static __x

#elif defined(XP_OS2) && !defined(JS_NO_DLL_DIRECTIVES)
#define JS_EXTERN_API(__type) extern __type
#define JS_EXPORT_API(__type) __type
#define JS_EXTERN_DATA(__type) extern __type
#define JS_EXPORT_DATA(__type) __type
#define JS_DLL_CALLBACK
#define JS_STATIC_DLL_CALLBACK(__x) __x _Optlink

#else /* Unix */
#define JS_EXTERN_API(__type) extern __type
#define JS_EXPORT_API(__type) __type
#define JS_EXTERN_DATA(__type) extern __type
#define JS_EXPORT_DATA(__type) __type
#define JS_DLL_CALLBACK
#define JS_STATIC_DLL_CALLBACK(__x) static __x

#endif

#if defined(_WIN32) && !defined(JS_NO_DLL_DIRECTIVES)
#    define JS_IMPORT_API(__x)      _declspec(dllimport) __x
#else
#    define JS_IMPORT_API(__x)      JS_EXPORT_API (__x)
#endif

#if defined(_WIN32) && !defined(JS_NO_DLL_DIRECTIVES)
#    define JS_IMPORT_DATA(__x)      _declspec(dllimport) __x
#else
#    define JS_IMPORT_DATA(__x)     __x
#endif

/*
 * The linkage of JS API functions differs depending on whether the file is
 * used within the JS library or not.  Any source file within the JS
 * interpreter should define EXPORT_JS_API whereas any client of the library
 * should not.
 */
#ifdef EXPORT_JS_API
#define JS_PUBLIC_API(t)    JS_EXPORT_API(t)
#define JS_PUBLIC_DATA(t)   JS_EXPORT_DATA(t)
#else
#define JS_PUBLIC_API(t)    JS_IMPORT_API(t)
#define JS_PUBLIC_DATA(t)   JS_IMPORT_DATA(t)
#endif

#define JS_FRIEND_API(t)    JS_PUBLIC_API(t)
#define JS_FRIEND_DATA(t)   JS_PUBLIC_DATA(t)

#ifdef _WIN32
#   define JS_INLINE __inline
#elif defined(__GNUC__)
#   define JS_INLINE
#else
#   define JS_INLINE
#endif

/***********************************************************************
** MACROS:      JS_BEGIN_MACRO
**              JS_END_MACRO
** DESCRIPTION:
**      Macro body brackets so that macros with compound statement definitions
**      behave syntactically more like functions when called.
***********************************************************************/
#define JS_BEGIN_MACRO  do {
#define JS_END_MACRO    } while (0)

/***********************************************************************
** MACROS:      JS_BEGIN_EXTERN_C
**              JS_END_EXTERN_C
** DESCRIPTION:
**      Macro shorthands for conditional C++ extern block delimiters.
***********************************************************************/
#ifdef __cplusplus
#define JS_BEGIN_EXTERN_C       extern "C" {
#define JS_END_EXTERN_C         }
#else
#define JS_BEGIN_EXTERN_C
#define JS_END_EXTERN_C
#endif

/***********************************************************************
** MACROS:      JS_BIT
**              JS_BITMASK
** DESCRIPTION:
** Bit masking macros.  XXX n must be <= 31 to be portable
***********************************************************************/
#define JS_BIT(n)       ((JSUint32)1 << (n))
#define JS_BITMASK(n)   (JS_BIT(n) - 1)

/***********************************************************************
** MACROS:      JS_ROUNDUP
**              JS_MIN
**                              JS_MAX
** DESCRIPTION:
**      Commonly used macros for operations on compatible types.
***********************************************************************/
#define JS_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
#define JS_MIN(x,y)     ((x)<(y)?(x):(y))
#define JS_MAX(x,y)     ((x)>(y)?(x):(y))

#if defined(XP_MAC) || defined(XP_PC)
#    include "jscpucfg.h"        /* Use standard Mac or Windows configuration */
#elif XP_UNIX
#    include "jsautocfg.h"       /* Use auto-detected configuration */
#else
#    error "Must define one of XP_PC, XP_MAC or XP_UNIX"
#endif

JS_BEGIN_EXTERN_C

/************************************************************************
** TYPES:       JSUint8
**              JSInt8
** DESCRIPTION:
**  The int8 types are known to be 8 bits each. There is no type that
**      is equivalent to a plain "char". 
************************************************************************/
#if JS_BYTES_PER_BYTE == 1
typedef unsigned char JSUint8;
typedef signed char JSInt8;
#else
#error No suitable type for JSInt8/JSUint8
#endif

/************************************************************************
** TYPES:       JSUint16
**              JSInt16
** DESCRIPTION:
**  The int16 types are known to be 16 bits each. 
************************************************************************/
#if JS_BYTES_PER_SHORT == 2
typedef unsigned short JSUint16;
typedef short JSInt16;
#else
#error No suitable type for JSInt16/JSUint16
#endif

/************************************************************************
** TYPES:       JSUint32
**              JSInt32
** DESCRIPTION:
**  The int32 types are known to be 32 bits each. 
************************************************************************/
#if JS_BYTES_PER_INT == 4
typedef unsigned int JSUint32;
typedef int JSInt32;
#define JS_INT32(x)  x
#define JS_UINT32(x) x ## U
#elif JS_BYTES_PER_LONG == 4
typedef unsigned long JSUint32;
typedef long JSInt32;
#define JS_INT32(x)  x ## L
#define JS_UINT32(x) x ## UL
#else
#error No suitable type for JSInt32/JSUint32
#endif

/************************************************************************
** TYPES:       JSUint64
**              JSInt64
** DESCRIPTION:
**  The int64 types are known to be 64 bits each. Care must be used when
**      declaring variables of type JSUint64 or JSInt64. Different hardware
**      architectures and even different compilers have varying support for
**      64 bit values. The only guaranteed portability requires the use of
**      the JSLL_ macros (see jslong.h).
************************************************************************/
#ifdef JS_HAVE_LONG_LONG
#if JS_BYTES_PER_LONG == 8
typedef long JSInt64;
typedef unsigned long JSUint64;
#elif defined(WIN16)
typedef __int64 JSInt64;
typedef unsigned __int64 JSUint64;
#elif defined(WIN32)
typedef __int64  JSInt64;
typedef unsigned __int64 JSUint64;
#else
typedef long long JSInt64;
typedef unsigned long long JSUint64;
#endif /* JS_BYTES_PER_LONG == 8 */
#else  /* !JS_HAVE_LONG_LONG */
typedef struct {
#ifdef IS_LITTLE_ENDIAN
    JSUint32 lo, hi;
#else
    JSUint32 hi, lo;
#endif
} JSInt64;
typedef JSInt64 JSUint64;
#endif /* !JS_HAVE_LONG_LONG */

/************************************************************************
** TYPES:       JSUintn
**              JSIntn
** DESCRIPTION:
**  The JSIntn types are most appropriate for automatic variables. They are
**      guaranteed to be at least 16 bits, though various architectures may
**      define them to be wider (e.g., 32 or even 64 bits). These types are
**      never valid for fields of a structure. 
************************************************************************/
#if JS_BYTES_PER_INT >= 2
typedef int JSIntn;
typedef unsigned int JSUintn;
#else
#error 'sizeof(int)' not sufficient for platform use
#endif

/************************************************************************
** TYPES:       JSFloat64
** DESCRIPTION:
**  NSPR's floating point type is always 64 bits. 
************************************************************************/
typedef double          JSFloat64;

/************************************************************************
** TYPES:       JSSize
** DESCRIPTION:
**  A type for representing the size of objects. 
************************************************************************/
typedef size_t JSSize;

/************************************************************************
** TYPES:       JSPtrDiff
** DESCRIPTION:
**  A type for pointer difference. Variables of this type are suitable
**      for storing a pointer or pointer sutraction. 
************************************************************************/
typedef ptrdiff_t JSPtrdiff;

/************************************************************************
** TYPES:       JSUptrdiff
** DESCRIPTION:
**  A type for pointer difference. Variables of this type are suitable
**      for storing a pointer or pointer sutraction. 
************************************************************************/
typedef unsigned long JSUptrdiff;

/************************************************************************
** TYPES:       JSBool
** DESCRIPTION:
**  Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE
**      for clarity of target type in assignments and actual arguments. Use
**      'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
**      juast as you would C int-valued conditions. 
************************************************************************/
typedef JSIntn JSBool;
#define JS_TRUE (JSIntn)1
#define JS_FALSE (JSIntn)0

/************************************************************************
** TYPES:       JSPackedBool
** DESCRIPTION:
**  Use PRPackedBOol within structs where bitfields are not desireable
**      but minimum and consistant overhead matters.
************************************************************************/
typedef JSUint8 JSPackedBool;

/*
** Status code used by some routines that have a single point of failure or 
** special status return.
*/
typedef enum { JS_FAILURE = -1, JS_SUCCESS = 0 } JSStatus;

/*
** A JSWord is an integer that is the same size as a void*
*/
typedef long JSWord;
typedef unsigned long JSUword;

#include "jsotypes.h"

JS_END_EXTERN_C

#endif /* jstypes_h___ */


**** End of jstypes.h. ****

**** Start of jsutil.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * PR assertion checker.
 */
#include <stdio.h>
#include <stdlib.h>
#include "jstypes.h"
#include "jsutil.h"

#ifdef WIN32
#    include <windows.h>
#endif

#ifdef XP_MAC
#	 include <Types.h>
#    include <stdarg.h>
#	 include "jsprf.h"
#endif

#ifdef XP_MAC
/*
 * PStrFromCStr converts the source C string to a destination
 * pascal string as it copies. The dest string will
 * be truncated to fit into an Str255 if necessary.
 * If the C String pointer is NULL, the pascal string's length is
 * set to zero.
 */
static void PStrFromCStr(const char* src, Str255 dst)
{
	short 	length  = 0;
	
	/* handle case of overlapping strings */
	if ( (void*)src == (void*)dst )
	{
		unsigned char*		curdst = &dst[1];
		unsigned char		thisChar;
				
		thisChar = *(const unsigned char*)src++;
		while ( thisChar != '\0' ) 
		{
			unsigned char	nextChar;
			
			/*
                         * Use nextChar so we don't overwrite what we
                         * are about to read
                         */
			nextChar = *(const unsigned char*)src++;
			*curdst++ = thisChar;
			thisChar = nextChar;
			
			if ( ++length >= 255 )
				break;
		}
	}
	else if ( src != NULL )
	{
		unsigned char*		curdst = &dst[1];
		/* count down so test it loop is faster */
		short 				overflow = 255;
		register char		temp;
	
		/*
                 * Can't do the K&R C thing of while (*s++ = *t++)
                 * because it will copy trailing zero which might
                 * overrun pascal buffer.  Instead we use a temp variable.
                 */
		while ( (temp = *src++) != 0 ) 
		{
			*(char*)curdst++ = temp;
				
			if ( --overflow <= 0 )
				break;
		}
		length = 255 - overflow;
	}
	dst[0] = length;
}

static void debugstr(const char *debuggerMsg)
{
	Str255		pStr;
	
	PStrFromCStr(debuggerMsg, pStr);
	DebugStr(pStr);
}

static void dprintf(const char *format, ...)
{
    va_list ap;
	char	*buffer;
	
	va_start(ap, format);
	buffer = (char *)JS_vsmprintf(format, ap);
	va_end(ap);
	
	debugstr(buffer);
	JS_DELETE(buffer);
}
#endif   /* XP_MAC */

#ifdef DEBUG
JS_EXPORT_API(void) JS_Assert(const char *s, const char *file, JSIntn ln)
{
#if defined(XP_UNIX) || defined(XP_OS2)
    fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
#endif
#ifdef XP_MAC
    dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln);
#endif
#ifdef WIN32
    DebugBreak();
#endif
#ifndef XP_MAC
    abort();
#endif
}
#endif

**** End of jsutil.c. ****

**** Start of jsutil.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * PR assertion checker.
 */

#ifndef jsutil_h___
#define jsutil_h___

JS_BEGIN_EXTERN_C

/***********************************************************************
** FUNCTION:	JS_MALLOC()
** DESCRIPTION:
**   JS_NEW() allocates an untyped item of size _size from the heap.
** INPUTS:  _size: size in bytes of item to be allocated
** OUTPUTS:	untyped pointer to the node allocated
** RETURN:	pointer to node or error returned from malloc().
***********************************************************************/
#define JS_MALLOC(_bytes) (malloc((_bytes)))

/***********************************************************************
** FUNCTION:	JS_DELETE()
** DESCRIPTION:
**   JS_DELETE() unallocates an object previosly allocated via JS_NEW()
**   or JS_NEWZAP() to the heap.
** INPUTS:	pointer to previously allocated object
** OUTPUTS:	the referenced object is returned to the heap
** RETURN:	void
***********************************************************************/
#define JS_DELETE(_ptr) { free(_ptr); (_ptr) = NULL; }

/***********************************************************************
** FUNCTION:	JS_NEW()
** DESCRIPTION:
**   JS_NEW() allocates an item of type _struct from the heap.
** INPUTS:  _struct: a data type
** OUTPUTS:	pointer to _struct
** RETURN:	pointer to _struct or error returns from malloc().
***********************************************************************/
#define JS_NEW(_struct) ((_struct *) JS_MALLOC(sizeof(_struct)))

#ifdef DEBUG

JS_EXTERN_API(void) JS_Assert(const char *s, const char *file, JSIntn ln);
#define JS_ASSERT(_expr) \
    ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__))

#define JS_NOT_REACHED(_reasonStr) \
    JS_Assert(_reasonStr,__FILE__,__LINE__)

#else

#define JS_ASSERT(expr) ((void) 0)
#define JS_NOT_REACHED(reasonStr)

#endif /* defined(DEBUG) */

/*
** Abort the process in a non-graceful manner. This will cause a core file,
** call to the debugger or other moral equivalent as well as causing the
** entire process to stop.
*/
JS_EXTERN_API(void) JS_Abort(void);

JS_END_EXTERN_C

#endif /* jsutil_h___ */

**** End of jsutil.h. ****

**** Start of jsxdrapi.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
#include "jsstddef.h"

#include <string.h>
#include "jstypes.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsprf.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsobj.h"		/* js_XDRObject */
#include "jsstr.h"
#include "jsxdrapi.h"

#ifdef DEBUG
#define DBG(x) x
#else
#define DBG(x) ((void)0)
#endif

typedef struct JSXDRMemState {
    JSXDRState  state;
    uint32      count;
    uint32      limit;
} JSXDRMemState;

#define MEM_BLOCK       8192
#define MEM_PRIV(xdr)   ((JSXDRMemState *)(xdr))

#define MEM_COUNT(xdr)  (MEM_PRIV(xdr)->count)
#define MEM_LIMIT(xdr)  (MEM_PRIV(xdr)->limit)


#define MEM_LEFT(xdr, bytes)                                                  \
    JS_BEGIN_MACRO                                                            \
	if ((xdr)->mode == JSXDR_DECODE &&                                    \
	    MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) {                        \
	    JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL,         \
				 JSMSG_END_OF_DATA);                          \
	    return 0;                                                         \
	}                                                                     \
    JS_END_MACRO

/* XXXbe why does NEED even allow or cope with non-ENCODE mode? */
#define MEM_NEED(xdr, bytes)                                                  \
    JS_BEGIN_MACRO                                                            \
	if ((xdr)->mode == JSXDR_ENCODE) {                                    \
	    if (MEM_LIMIT(xdr) &&                                             \
		MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) {                    \
		void *_data = JS_realloc((xdr)->cx,                           \
					 (xdr)->data,                         \
					 MEM_LIMIT(xdr) + MEM_BLOCK);         \
		if (!_data)                                                   \
		    return 0;                                                 \
		(xdr)->data = _data;                                          \
		MEM_LIMIT(xdr) += MEM_BLOCK;                                  \
	    }                                                                 \
	} else {                                                              \
	    if (MEM_LIMIT(xdr) < MEM_COUNT(xdr) + bytes) {                    \
		JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL,     \
				     JSMSG_END_OF_DATA);                      \
		return 0;                                                     \
	    }                                                                 \
	}                                                                     \
    JS_END_MACRO

#define MEM_DATA(xdr)        ((void *)((char *)(xdr)->data + MEM_COUNT(xdr)))
#define MEM_INCR(xdr,bytes)  (MEM_COUNT(xdr) += (bytes))

static JSBool
mem_get32(JSXDRState *xdr, uint32 *lp)
{
    MEM_LEFT(xdr, 4);
    *lp = *(uint32 *)MEM_DATA(xdr);
    MEM_INCR(xdr, 4);
    return JS_TRUE;
}

static JSBool
mem_set32(JSXDRState *xdr, uint32 *lp)
{
    MEM_NEED(xdr, 4);
    *(uint32 *)MEM_DATA(xdr) = *lp;
    MEM_INCR(xdr, 4);
    return JS_TRUE;
}

static JSBool
mem_getbytes(JSXDRState *xdr, char **bytesp, uint32 len)
{
    MEM_LEFT(xdr, len);
    memcpy(*bytesp, MEM_DATA(xdr), len);
    MEM_INCR(xdr, len);
    return JS_TRUE;
}

static JSBool
mem_setbytes(JSXDRState *xdr, char **bytesp, uint32 len)
{
    MEM_NEED(xdr, len);
    memcpy(MEM_DATA(xdr), *bytesp, len);
    MEM_INCR(xdr, len);
    return JS_TRUE;
}

static void *
mem_raw(JSXDRState *xdr, uint32 len)
{
    void *data;
    if (xdr->mode == JSXDR_ENCODE) {
	MEM_NEED(xdr, len);
    } else if (xdr->mode == JSXDR_DECODE) {
	MEM_LEFT(xdr, len);
    }
    data = MEM_DATA(xdr);
    MEM_INCR(xdr, len);
    return data;
}

static JSBool
mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence)
{
    switch (whence) {
      case JSXDR_SEEK_CUR:
	if ((int32)MEM_COUNT(xdr) + offset < 0) {
	    JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
				 JSMSG_SEEK_BEYOND_START);
	    return JS_FALSE;
	}
	if (offset > 0)
	    MEM_NEED(xdr, offset);
	MEM_COUNT(xdr) += offset;
	return JS_TRUE;
      case JSXDR_SEEK_SET:
	if (offset < 0) {
	    JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
				 JSMSG_SEEK_BEYOND_START);
	    return JS_FALSE;
	}
	if (xdr->mode == JSXDR_ENCODE) {
	    if ((uint32)offset > MEM_COUNT(xdr))
		MEM_NEED(xdr, offset - MEM_COUNT(xdr));
	    MEM_COUNT(xdr) = offset;
	} else {
	    if ((uint32)offset > MEM_LIMIT(xdr)) {
		JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
				     JSMSG_SEEK_BEYOND_END);
		return JS_FALSE;
	    }
	    MEM_COUNT(xdr) = offset;
	}
	return JS_TRUE;
      case JSXDR_SEEK_END:
	if (offset >= 0 ||
	    xdr->mode == JSXDR_ENCODE ||
	    (int32)MEM_LIMIT(xdr) + offset < 0) {
	    JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
				 JSMSG_END_SEEK);
	    return JS_FALSE;
	}
	MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset;
	return JS_TRUE;
      default: {
	char numBuf[12];
	JS_snprintf(numBuf, sizeof numBuf, "%d", whence);
	JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
			     JSMSG_WHITHER_WHENCE, numBuf);
	return JS_FALSE;
      }
    }
}

static uint32
mem_tell(JSXDRState *xdr)
{
    return MEM_COUNT(xdr);
}

static void
mem_finalize(JSXDRState *xdr)
{
    JSContext *cx = xdr->cx;
    JS_free(cx, xdr->data);
}

static JSXDROps xdrmem_ops = {
    mem_get32,      mem_set32,      mem_getbytes,   mem_setbytes,
    mem_raw,        mem_seek,       mem_tell,       mem_finalize
};

JS_PUBLIC_API(void)
JS_XDRNewBase(JSContext *cx, JSXDRState *xdr, JSXDRMode mode)
{
    xdr->cx = cx;
    xdr->mode = mode;
    xdr->registry = NULL;
    xdr->nclasses = 0;
}

JS_PUBLIC_API(JSXDRState *)
JS_XDRNewMem(JSContext *cx, JSXDRMode mode)
{
    JSXDRState *xdr = JS_malloc(cx, sizeof(JSXDRMemState));
    if (!xdr)
	return NULL;
    JS_XDRNewBase(cx, xdr, mode);
    if (mode == JSXDR_ENCODE) {
	if (!(xdr->data = JS_malloc(cx, MEM_BLOCK))) {
	    JS_free(cx, xdr);
	    return NULL;
	}
    } else {
	/* XXXbe ok, so better not deref xdr->data if not ENCODE */
	xdr->data = NULL;
    }
    xdr->ops = &xdrmem_ops;
    MEM_PRIV(xdr)->count = 0;
    MEM_PRIV(xdr)->limit = MEM_BLOCK;
    return xdr;
}

JS_PUBLIC_API(void *)
JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp)
{
    if (xdr->ops != &xdrmem_ops)
	return NULL;
    *lp = MEM_PRIV(xdr)->count;
    return xdr->data;
}

JS_PUBLIC_API(void)
JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len)
{
    if (xdr->ops != &xdrmem_ops)
	return;
    MEM_PRIV(xdr)->limit = len;
    xdr->data = data;
    MEM_PRIV(xdr)->count = 0;
}

JS_PUBLIC_API(JSBool)
JS_XDRUint8(JSXDRState *xdr, uint8 *b)
{
    uint32 l = *b;
    if (!JS_XDRUint32(xdr, &l))
	return JS_FALSE;
    *b = (uint8) l & 0xff;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_XDRUint16(JSXDRState *xdr, uint16 *s)
{
    uint32 l = *s;
    if (!JS_XDRUint32(xdr, &l))
	return JS_FALSE;
    *s = (uint16) l & 0xffff;
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_XDRUint32(JSXDRState *xdr, uint32 *lp)
{
    JSBool ok = JS_FALSE;
    if (xdr->mode == JSXDR_ENCODE) {
	uint32 xl = JSXDR_SWAB32(*lp);
	ok = xdr->ops->set32(xdr, &xl);
    } else if (xdr->mode == JSXDR_DECODE) {
	ok = xdr->ops->get32(xdr, lp);
	*lp = JSXDR_SWAB32(*lp);
    }
    return ok;
}

JS_PUBLIC_API(JSBool)
JS_XDRBytes(JSXDRState *xdr, char **bytesp, uint32 len)
{
    if (xdr->mode == JSXDR_ENCODE) {
	if (!xdr->ops->setbytes(xdr, bytesp, len))
	    return JS_FALSE;
    } else {
	if (!xdr->ops->getbytes(xdr, bytesp, len))
	    return JS_FALSE;
    }
    len = xdr->ops->tell(xdr);
    if (len % JSXDR_ALIGN) {
	if (!xdr->ops->seek(xdr, JSXDR_ALIGN - (len % JSXDR_ALIGN),
			    JSXDR_SEEK_CUR)) {
	    return JS_FALSE;
	}
    }
    return JS_TRUE;
}

/**
 * Convert between a C string and the XDR representation:
 * leading 32-bit count, then counted vector of chars,
 * then possibly \0 padding to multiple of 4.
 */
JS_PUBLIC_API(JSBool)
JS_XDRCString(JSXDRState *xdr, char **sp)
{
    uint32 len;

    if (xdr->mode == JSXDR_ENCODE)
	len = strlen(*sp);
    JS_XDRUint32(xdr, &len);
    if (xdr->mode == JSXDR_DECODE) {
	if (!(*sp = JS_malloc(xdr->cx, len + 1)))
	    return JS_FALSE;
    }
    if (!JS_XDRBytes(xdr, sp, len)) {
	if (xdr->mode == JSXDR_DECODE)
	    JS_free(xdr->cx, *sp);
	return JS_FALSE;
    }
    if (xdr->mode == JSXDR_DECODE) {
	(*sp)[len] = '\0';
    } else if (xdr->mode == JSXDR_FREE) {
	JS_free(xdr->cx, *sp);
	*sp = NULL;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp)
{
    uint32 null = (*sp == NULL);
    if (!JS_XDRUint32(xdr, &null))
	return JS_FALSE;
    if (null) {
	*sp = NULL;
	return JS_TRUE;
    }
    return JS_XDRCString(xdr, sp);
}

/*
 * Convert between a JS (Unicode) string and the XDR representation.
 */
JS_PUBLIC_API(JSBool)
JS_XDRString(JSXDRState *xdr, JSString **strp)
{
    uint32 i, len, nbytes;
    jschar *chars = NULL, *raw;

    if (xdr->mode == JSXDR_ENCODE)
	len = (*strp)->length;
    if (!JS_XDRUint32(xdr, &len))
	return JS_FALSE;
    nbytes = len * sizeof(jschar);

    if (xdr->mode == JSXDR_ENCODE) {
	chars = (*strp)->chars;
    } else if (xdr->mode == JSXDR_DECODE) {
	if (!(chars = JS_malloc(xdr->cx, nbytes + sizeof(jschar))))
	    return JS_FALSE;
    }

    if (nbytes % JSXDR_ALIGN)
	nbytes += JSXDR_ALIGN - (nbytes % JSXDR_ALIGN);
    if (!(raw = xdr->ops->raw(xdr, nbytes)))
	goto bad;
    if (xdr->mode == JSXDR_ENCODE) {
	for (i = 0; i < len; i++)
	    raw[i] = JSXDR_SWAB16(chars[i]);
    } else if (xdr->mode == JSXDR_DECODE) {
	for (i = 0; i < len; i++)
	    chars[i] = JSXDR_SWAB16(raw[i]);
	if (!(*strp = JS_NewUCString(xdr->cx, chars, len)))
	    goto bad;
    }
    return JS_TRUE;

bad:
    if (xdr->mode == JSXDR_DECODE)
	JS_free(xdr->cx, chars);
    return JS_FALSE;
}

JS_PUBLIC_API(JSBool)
JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp)
{
    uint32 null = (*strp == NULL);
    if (!JS_XDRUint32(xdr, &null))
	return JS_FALSE;
    if (null) {
	*strp = NULL;
	return JS_TRUE;
    }
    return JS_XDRString(xdr, strp);
}

JS_PUBLIC_API(JSBool)
JS_XDRDouble(JSXDRState *xdr, jsdouble **dp)
{
    jsdouble d;
    if (xdr->mode == JSXDR_ENCODE)
	d = **dp;
#if IS_BIG_ENDIAN
    if (!JS_XDRUint32(xdr, (uint32 *)&d + 1) ||
	!JS_XDRUint32(xdr, (uint32 *)&d))
#else /* !IS_BIG_ENDIAN */
    if (!JS_XDRUint32(xdr, (uint32 *)&d) ||
	!JS_XDRUint32(xdr, (uint32 *)&d + 1))
#endif /* IS_BIG_ENDIAN */
	return JS_FALSE;
    if (xdr->mode == JSXDR_DECODE) {
	*dp = JS_NewDouble(xdr->cx, d);
	if (!*dp)
	    return JS_FALSE;
    }
    return JS_TRUE;
}

JS_PUBLIC_API(JSBool)
JS_XDRValue(JSXDRState *xdr, jsval *vp)
{
    uint32 type = JSVAL_TAG(*vp);
    if (!JS_XDRUint32(xdr, &type))
	return JS_FALSE;

    switch (type) {
      case JSVAL_STRING: {
	JSString *str = JSVAL_TO_STRING(*vp);
	if (!JS_XDRString(xdr, &str))
	    return JS_FALSE;
	if (xdr->mode == JSXDR_DECODE)
	    *vp = STRING_TO_JSVAL(str);
	break;
      }
      case JSVAL_DOUBLE: {
	jsdouble *dp;
	if (xdr->mode == JSXDR_ENCODE)
	    dp = JSVAL_TO_DOUBLE(*vp);
	if (!JS_XDRDouble(xdr, &dp))
	    return JS_FALSE;
	if (xdr->mode == JSXDR_DECODE)
	    *vp = DOUBLE_TO_JSVAL(dp);
	break;
      }
      case JSVAL_OBJECT: {
	JSObject *obj;
	if (xdr->mode == JSXDR_ENCODE)
	    obj = JSVAL_TO_OBJECT(*vp);
	if (!js_XDRObject(xdr, &obj))
	    return JS_FALSE;
	if (xdr->mode == JSXDR_DECODE)
	    *vp = OBJECT_TO_JSVAL(obj);
	break;
      }
      case JSVAL_BOOLEAN: {
	uint32 b;
	if (xdr->mode == JSXDR_ENCODE)
	    b = (uint32)JSVAL_TO_BOOLEAN(*vp);
	if (!JS_XDRUint32(xdr, &b))
	    return JS_FALSE;
	if (xdr->mode == JSXDR_DECODE)
	    *vp = BOOLEAN_TO_JSVAL((JSBool)b);
	break;
      }
      case JSVAL_VOID:
	if (!JS_XDRUint32(xdr, (uint32 *)vp))
	    return JS_FALSE;
	break;
      default: {
	char numBuf[12];
	if (type & JSVAL_INT) {
	    uint32 i;
	    if (xdr->mode == JSXDR_ENCODE)
		i = JSVAL_TO_INT(*vp);
	    if (!JS_XDRUint32(xdr, &i))
		return JS_FALSE;
	    if (xdr->mode == JSXDR_DECODE)
		*vp = INT_TO_JSVAL(i);
	    break;
	}
	JS_snprintf(numBuf, sizeof numBuf, "%#lx", type);
	JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
			     JSMSG_BAD_JVAL_TYPE, type);
	return JS_FALSE;
      }
    }
    return JS_TRUE;
}


JS_PUBLIC_API(void)
JS_XDRDestroy(JSXDRState *xdr)
{
    JSContext *cx = xdr->cx;
    xdr->ops->finalize(xdr);
    if (xdr->registry)
	JS_free(cx, xdr->registry);
    JS_free(cx, xdr);
}

#define REGISTRY_CHUNK 4

JS_PUBLIC_API(JSBool)
JS_RegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp)
{
    uintN nclasses;
    JSClass **registry;

    nclasses = xdr->nclasses;
    if (nclasses == 0) {
	registry = JS_malloc(xdr->cx, REGISTRY_CHUNK * sizeof(JSClass *));
    } else if (nclasses % REGISTRY_CHUNK == 0) {
	registry = JS_realloc(xdr->cx,
			      xdr->registry,
			      (nclasses + REGISTRY_CHUNK) * sizeof(JSClass *));
    } else {
        registry = xdr->registry;
    }
    if (!registry)
	return JS_FALSE;
    registry[nclasses++] = clasp;
    xdr->registry = registry;
    xdr->nclasses = nclasses;
    *idp = nclasses;
    return JS_TRUE;
}

JS_PUBLIC_API(uint32)
JS_FindClassIdByName(JSXDRState *xdr, const char *name)
{
    uintN i;

    for (i = 0; i < xdr->nclasses; i++) {
	if (!strcmp(name, xdr->registry[i]->name))
	    return i+1;
    }
    return 0;
}

JS_PUBLIC_API(JSClass *)
JS_FindClassById(JSXDRState *xdr, uint32 id)
{
    if (id > xdr->nclasses)
	return NULL;
    return xdr->registry[id-1];
}

**** End of jsxdrapi.c. ****

**** Start of jsxdrapi.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef jsxdrapi_h___
#define jsxdrapi_h___

/*
 * JS external data representation interface API.
 *
 * The XDR system is comprised of three major parts:
 *
 * - the state serialization/deserialization APIs, which allow consumers
 *   of the API to serialize JS runtime state (script bytecodes, atom maps,
 *   object graphs, etc.) for later restoration.  These portions
 *   are implemented in various appropriate files, such as jsscript.c
 *   for the script portions and jsobj.c for object state.
 * - the callback APIs through which the runtime requests an opaque
 *   representation of a native object, and through which the runtime
 *   constructs a live native object from an opaque representation. These
 *   portions are the responsibility of the native object implementor.
 * - utility functions for en/decoding of primitive types, such as
 *   JSStrings.  This portion is implemented in jsxdrapi.c.
 *
 * Spiritually guided by Sun's XDR, where appropriate.
 */

#include "jspubtd.h"
#include "jsprvtd.h"

JS_BEGIN_EXTERN_C

/* We use little-endian byteorder for all encoded data */

#if defined IS_LITTLE_ENDIAN
#define JSXDR_SWAB32(x) x
#define JSXDR_SWAB16(x) x
#elif defined IS_BIG_ENDIAN
#define JSXDR_SWAB32(x) (((x) >> 24) |                                        \
			 (((x) >> 8) & 0xff00) |                              \
			 (((x) << 8) & 0xff0000) |                            \
			 ((x) << 24))
#define JSXDR_SWAB16(x) (((x) >> 8) | ((x) << 8))
#else
#error "unknown byte order"
#endif

#define JSXDR_ALIGN     4

typedef enum JSXDRMode {
    JSXDR_ENCODE,
    JSXDR_DECODE,
    JSXDR_FREE
} JSXDRMode;

typedef enum JSXDRWhence {
    JSXDR_SEEK_SET,
    JSXDR_SEEK_CUR,
    JSXDR_SEEK_END
} JSXDRWhence;

typedef struct JSXDROps {
    JSBool      (*get32)(JSXDRState *, uint32 *);
    JSBool      (*set32)(JSXDRState *, uint32 *);
    JSBool      (*getbytes)(JSXDRState *, char **, uint32);
    JSBool      (*setbytes)(JSXDRState *, char **, uint32);
    void *      (*raw)(JSXDRState *, uint32);
    JSBool      (*seek)(JSXDRState *, int32, JSXDRWhence);
    uint32      (*tell)(JSXDRState *);
    void        (*finalize)(JSXDRState *);
} JSXDROps;

struct JSXDRState {
    JSXDRMode   mode;
    JSXDROps    *ops;
    JSContext   *cx;
    JSClass     **registry;
    uintN       nclasses;
    void        *data;
};

JS_PUBLIC_API(void)
JS_XDRNewBase(JSContext *cx, JSXDRState *xdr, JSXDRMode mode);

JS_PUBLIC_API(JSXDRState *)
JS_XDRNewMem(JSContext *cx, JSXDRMode mode);

JS_PUBLIC_API(void *)
JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp);

JS_PUBLIC_API(void)
JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len);

JS_PUBLIC_API(void)
JS_XDRDestroy(JSXDRState *xdr);

JS_PUBLIC_API(JSBool)
JS_XDRUint8(JSXDRState *xdr, uint8 *b);

JS_PUBLIC_API(JSBool)
JS_XDRUint16(JSXDRState *xdr, uint16 *s);

JS_PUBLIC_API(JSBool)
JS_XDRUint32(JSXDRState *xdr, uint32 *lp);

JS_PUBLIC_API(JSBool)
JS_XDRBytes(JSXDRState *xdr, char **bytes, uint32 len);

JS_PUBLIC_API(JSBool)
JS_XDRCString(JSXDRState *xdr, char **sp);

JS_PUBLIC_API(JSBool)
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp);

JS_PUBLIC_API(JSBool)
JS_XDRString(JSXDRState *xdr, JSString **strp);

JS_PUBLIC_API(JSBool)
JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp);

JS_PUBLIC_API(JSBool)
JS_XDRDouble(JSXDRState *xdr, jsdouble **dp);

JS_PUBLIC_API(JSBool)
JS_XDRValue(JSXDRState *xdr, jsval *vp);

JS_PUBLIC_API(JSBool)
JS_RegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp);

JS_PUBLIC_API(uint32)
JS_FindClassIdByName(JSXDRState *xdr, const char *name);

JS_PUBLIC_API(JSClass *)
JS_FindClassById(JSXDRState *xdr, uint32 id);

/* Magic values */

#define OBJ_XDRMAGIC            0xdead1000
#define OBJ_XDRTYPE_OBJ         0xdead1001
#define OBJ_XDRTYPE_FUN         0xdead1002
#define OBJ_XDRTYPE_REGEXP      0xdead1003
#define SCRIPT_XDRMAGIC         0xdead0001

#endif /* ! jsxdrapi_h___ */

**** End of jsxdrapi.h. ****

**** Start of prmjtime.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/*
 * PR time code.
 */
#ifdef SOLARIS
#define _REENTRANT 1
#endif
#include <string.h>
#include <time.h>
#include "jstypes.h"

#include "jsprf.h"
#include "prmjtime.h"

#define PRMJ_DO_MILLISECONDS 1

#ifdef XP_PC
#include <sys/timeb.h>
#endif

#ifdef XP_MAC
#include <OSUtils.h>
#include <TextUtils.h>
#include <Resources.h>
#include <Timer.h>
#endif

#ifdef XP_UNIX

#ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
extern int gettimeofday(struct timeval *tv);
#endif

#include <sys/time.h>

#endif /* XP_UNIX */

#ifdef XP_MAC
#if JS_HAVE_LONG_LONG
static JSUint64			dstLocalBaseMicroseconds;
#else
static UnsignedWide		dstLocalBaseMicroseconds;
#endif
static unsigned long	gJanuaryFirst1970Seconds;

static void MacintoshInitializeTime(void)
{
	UnsignedWide			upTime;
	unsigned long			currentLocalTimeSeconds,
							startupTimeSeconds;
	uint64					startupTimeMicroSeconds;
	uint32					upTimeSeconds;
	uint64					oneMillion, upTimeSecondsLong, microSecondsToSeconds;
	DateTimeRec				firstSecondOfUnixTime;

	//	Figure out in local time what time the machine
	//	started up.  This information can be added to
	//	upTime to figure out the current local time
	//	as well as GMT.

	Microseconds(&upTime);

	GetDateTime(&currentLocalTimeSeconds);

	JSLL_I2L(microSecondsToSeconds, PRMJ_USEC_PER_SEC);
	JSLL_DIV(upTimeSecondsLong,  *((uint64 *)&upTime), microSecondsToSeconds);
	JSLL_L2I(upTimeSeconds, upTimeSecondsLong);

	startupTimeSeconds = currentLocalTimeSeconds - upTimeSeconds;

	//	Make sure that we normalize the macintosh base seconds
	//	to the unix base of January 1, 1970.

	firstSecondOfUnixTime.year = 1970;
	firstSecondOfUnixTime.month = 1;
	firstSecondOfUnixTime.day = 1;
	firstSecondOfUnixTime.hour = 0;
	firstSecondOfUnixTime.minute = 0;
	firstSecondOfUnixTime.second = 0;
	firstSecondOfUnixTime.dayOfWeek = 0;

	DateToSeconds(&firstSecondOfUnixTime, &gJanuaryFirst1970Seconds);

	startupTimeSeconds -= gJanuaryFirst1970Seconds;

	//	Now convert the startup time into a wide so that we
	//	can figure out GMT and DST.

	JSLL_I2L(startupTimeMicroSeconds, startupTimeSeconds);
	JSLL_I2L(oneMillion, PRMJ_USEC_PER_SEC);
	JSLL_MUL(dstLocalBaseMicroseconds, oneMillion, startupTimeMicroSeconds);
}

// Because serial port and SLIP conflict with ReadXPram calls,
// we cache the call here

static void MyReadLocation(MachineLocation * loc)
{
	static MachineLocation storedLoc;	// InsideMac, OSUtilities, page 4-20
	static JSBool didReadLocation = JS_FALSE;
	if (!didReadLocation)
	{
		MacintoshInitializeTime();
		ReadLocation(&storedLoc);
		didReadLocation = JS_TRUE;
	}
	*loc = storedLoc;
}
#endif /* XP_MAC */

#define IS_LEAP(year) \
   (year != 0 && ((((year & 0x3) == 0) &&  \
		   ((year - ((year/100) * 100)) != 0)) || \
		  (year - ((year/400) * 400)) == 0))

#define PRMJ_HOUR_SECONDS  3600L
#define PRMJ_DAY_SECONDS  (24L * PRMJ_HOUR_SECONDS)
#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L)
#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
/* function prototypes */
static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
/*
 * get the difference in seconds between this time zone and UTC (GMT)
 */
time_t
PRMJ_LocalGMTDifference()
{
#if defined(XP_UNIX) || defined(XP_PC)
    struct tm ltime;

    /* get the difference between this time zone and GMT */
    memset((char *)&ltime,0,sizeof(ltime));
    ltime.tm_mday = 2;
    ltime.tm_year = 70;
#ifdef SUNOS4
    ltime.tm_zone = 0;
    ltime.tm_gmtoff = 0;
    return timelocal(&ltime) - (24 * 3600);
#else
    return mktime(&ltime) - (24L * 3600L);
#endif
#endif
#if defined(XP_MAC)
    static time_t    zone = -1L;
    MachineLocation  machineLocation;
    JSUint64	     gmtOffsetSeconds;
    JSUint64	     gmtDelta;
    JSUint64	     dlsOffset;
    JSInt32	     offset;

    /* difference has been set no need to recalculate */
    if(zone != -1)
	return zone;

    /* Get the information about the local machine, including
     * its GMT offset and its daylight savings time info.
     * Convert each into wides that we can add to
     * startupTimeMicroSeconds.
     */

    MyReadLocation(&machineLocation);

    /* Mask off top eight bits of gmtDelta, sign extend lower three. */

    if ((machineLocation.u.gmtDelta & 0x00800000) != 0) {
#if JS_HAVE_LONG_LONG
	gmtOffsetSeconds = (machineLocation.u.gmtDelta & 0x00FFFFFF) | 0xFFFFFFFFFF000000LL;
#else
	gmtOffsetSeconds.lo = (machineLocation.u.gmtDelta & 0x00FFFFFF) | 0xFF000000;
	gmtOffsetSeconds.hi = 0xFFFFFFFF;
#endif
	JSLL_UI2L(gmtDelta,0);
    } else {
#if JS_HAVE_LONG_LONG
	gmtOffsetSeconds = (machineLocation.u.gmtDelta & 0x00FFFFFF);
#else
	gmtOffsetSeconds.lo = (machineLocation.u.gmtDelta & 0x00FFFFFF);
	gmtOffsetSeconds.hi = 0;
#endif
	JSLL_UI2L(gmtDelta,PRMJ_DAY_SECONDS);
    }

    /*
     * Normalize time to be positive if you are behind GMT. gmtDelta will
     * always be positive.
     */
    JSLL_SUB(gmtDelta,gmtDelta,gmtOffsetSeconds);

    /* Is Daylight Savings On?  If so, we need to add an hour to the offset. */
    if (machineLocation.u.dlsDelta != 0) {
	JSLL_UI2L(dlsOffset, PRMJ_HOUR_SECONDS);
    } else {
	JSLL_I2L(dlsOffset, 0);
    }

    JSLL_ADD(gmtDelta,gmtDelta, dlsOffset);
    JSLL_L2I(offset,gmtDelta);

    zone = offset;
    return (time_t)offset;
#endif
}

/* Constants for GMT offset from 1970 */
#define G1970GMTMICROHI        0x00dcdcad /* micro secs to 1970 hi */
#define G1970GMTMICROLOW       0x8b3fa000 /* micro secs to 1970 low */

#define G2037GMTMICROHI        0x00e45fab /* micro secs to 2037 high */
#define G2037GMTMICROLOW       0x7a238000 /* micro secs to 2037 low */

/* Convert from base time to extended time */
static JSInt64
PRMJ_ToExtendedTime(JSInt32 time)
{
    JSInt64 exttime;
    JSInt64 g1970GMTMicroSeconds;
    JSInt64 low;
    time_t diff;
    JSInt64  tmp;
    JSInt64  tmp1;

    diff = PRMJ_LocalGMTDifference();
    JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
    JSLL_I2L(tmp1,diff);
    JSLL_MUL(tmp,tmp,tmp1);

    JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
    JSLL_UI2L(low,G1970GMTMICROLOW);
#ifndef JS_HAVE_LONG_LONG
    JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
    JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
#else
    JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32);
#endif
    JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);

    JSLL_I2L(exttime,time);
    JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
    JSLL_SUB(exttime,exttime,tmp);
    return exttime;
}

JSInt64
PRMJ_Now(void)
{
#ifdef XP_PC
    JSInt64 s, us, ms2us, s2us;
    struct timeb b;
#endif /* XP_PC */
#ifdef XP_UNIX
    struct timeval tv;
    JSInt64 s, us, s2us;
#endif /* XP_UNIX */
#ifdef XP_MAC
    UnsignedWide upTime;
    JSInt64	 localTime;
    JSInt64       gmtOffset;
    JSInt64    dstOffset;
    time_t       gmtDiff;
    JSInt64	 s2us;
#endif /* XP_MAC */

#ifdef XP_PC
    ftime(&b);
    JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC);
    JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
    JSLL_UI2L(s, b.time);
    JSLL_UI2L(us, b.millitm);
    JSLL_MUL(us, us, ms2us);
    JSLL_MUL(s, s, s2us);
    JSLL_ADD(s, s, us);
    return s;
#endif

#ifdef XP_UNIX
#ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
    gettimeofday(&tv);
#else
    gettimeofday(&tv, 0);
#endif /* _SVID_GETTOD */
    JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
    JSLL_UI2L(s, tv.tv_sec);
    JSLL_UI2L(us, tv.tv_usec);
    JSLL_MUL(s, s, s2us);
    JSLL_ADD(s, s, us);
    return s;
#endif /* XP_UNIX */
#ifdef XP_MAC
    JSLL_UI2L(localTime,0);
    gmtDiff = PRMJ_LocalGMTDifference();
    JSLL_I2L(gmtOffset,gmtDiff);
    JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
    JSLL_MUL(gmtOffset,gmtOffset,s2us);
    JSLL_UI2L(dstOffset,0);
    dstOffset = PRMJ_DSTOffset(dstOffset);
    JSLL_SUB(gmtOffset,gmtOffset,dstOffset);
    /* don't adjust for DST since it sets ctime and gmtime off on the MAC */
    Microseconds(&upTime);
    JSLL_ADD(localTime,localTime,gmtOffset);
    JSLL_ADD(localTime,localTime, *((JSUint64 *)&dstLocalBaseMicroseconds));
    JSLL_ADD(localTime,localTime, *((JSUint64 *)&upTime));

    return *((JSUint64 *)&localTime);
#endif /* XP_MAC */
}

/* Get the DST timezone offset for the time passed in */
JSInt64
PRMJ_DSTOffset(JSInt64 time)
{
    JSInt64 us2s;
#ifdef XP_MAC
    MachineLocation  machineLocation;
    JSInt64 dlsOffset;
    /*	Get the information about the local machine, including
     *	its GMT offset and its daylight savings time info.
     *	Convert each into wides that we can add to
     *	startupTimeMicroSeconds.
     */
    MyReadLocation(&machineLocation);

    /* Is Daylight Savings On?  If so, we need to add an hour to the offset. */
    if (machineLocation.u.dlsDelta != 0) {
	JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); /* seconds in a microseconds */
	JSLL_UI2L(dlsOffset, PRMJ_HOUR_SECONDS);  /* seconds in one hour       */
	JSLL_MUL(dlsOffset, dlsOffset, us2s);
    } else {
	JSLL_I2L(dlsOffset, 0);
    }
    return(dlsOffset);
#else
    time_t local;
    JSInt32 diff;
    JSInt64  maxtimet;
    struct tm tm;
    PRMJTime prtm;
#if defined( XP_PC ) || defined( FREEBSD ) || defined ( HPUX9 ) || defined ( SNI ) || defined ( NETBSD ) || defined ( OPENBSD ) || defined( RHAPSODY )
    struct tm *ptm;
#endif


    JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
    JSLL_DIV(time, time, us2s);

    /* get the maximum of time_t value */
    JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);

    if(JSLL_CMP(time,>,maxtimet)){
      JSLL_UI2L(time,PRMJ_MAX_UNIX_TIMET);
    } else if(!JSLL_GE_ZERO(time)){
      /*go ahead a day to make localtime work (does not work with 0) */
      JSLL_UI2L(time,PRMJ_DAY_SECONDS);
    }
    JSLL_L2UI(local,time);
    PRMJ_basetime(time,&prtm);
#if defined( XP_PC ) || defined( FREEBSD ) || defined ( HPUX9 ) || defined ( SNI ) || defined ( NETBSD ) || defined ( OPENBSD ) || defined( RHAPSODY )
    ptm = localtime(&local);
    if(!ptm){
      return JSLL_ZERO;
    }
    tm = *ptm;
#else
    localtime_r(&local,&tm); /* get dst information */
#endif

    diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
	((tm.tm_min - prtm.tm_min) * 60);

    if(diff < 0){
	diff += PRMJ_DAY_SECONDS;
    }

    JSLL_UI2L(time,diff);

    JSLL_MUL(time,time,us2s);

    return(time);
#endif
}

/* Format a time value into a buffer. Same semantics as strftime() */
size_t
PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm)
{
#if defined(XP_UNIX) || defined(XP_PC) || defined(XP_MAC)
    struct tm a;

    /* Zero out the tm struct.  Linux, SunOS 4 struct tm has extra members int
     * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets
     * confused and dumps core.  NSPR20 prtime.c attempts to fill these in by
     * calling mktime on the partially filled struct, but this doesn't seem to
     * work as well; the result string has "can't get timezone" for ECMA-valid
     * years.  Might still make sense to use this, but find the range of years
     * for which valid tz information exists, and map (per ECMA hint) from the
     * given year into that range.
     
     * N.B. This hasn't been tested with anything that actually _uses_
     * tm_gmtoff; zero might be the wrong thing to set it to if you really need
     * to format a time.  This fix is for jsdate.c, which only uses
     * JS_FormatTime to get a string representing the time zone.  */
    memset(&a, 0, sizeof(struct tm));

    a.tm_sec = prtm->tm_sec;
    a.tm_min = prtm->tm_min;
    a.tm_hour = prtm->tm_hour;
    a.tm_mday = prtm->tm_mday;
    a.tm_mon = prtm->tm_mon;
    a.tm_wday = prtm->tm_wday;
    a.tm_year = prtm->tm_year - 1900;
    a.tm_yday = prtm->tm_yday;
    a.tm_isdst = prtm->tm_isdst;

    /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
     * are null.  This doesn't quite work, though - the timezone is off by
     * tzoff + dst.  (And mktime seems to return -1 for the exact dst
     * changeover time.)

     * Still not sure if MKLINUX is necessary; this is borrowed from the NSPR20
     * prtime.c.  I'm leaving it out - My Linux does the right thing without it
     * (and the wrong thing with it) even though it has the tm_gmtoff, tm_zone
     * fields.  Linux seems to be happy so long as the tm struct is zeroed out.
     * The #ifdef in nspr is:
     * #if defined(SUNOS4) || defined(MKLINUX) || defined (__GLIBC >= 2)
     */

#if defined(SUNOS4)
    if (mktime(&a) == -1) {
        /* Seems to fail whenever the requested date is outside of the 32-bit
         * UNIX epoch.  We could proceed at this point (setting a.tm_zone to
         * "") but then strftime returns a string with a 2-digit field of
         * garbage for the year.  So we return 0 and hope jsdate.c
         * will fall back on toString.
         */
        return 0;
    }
#endif

    return strftime(buf, buflen, fmt, &a);
#endif
}

/* table for number of days in a month */
static int mtab[] = {
  /* jan, feb,mar,apr,may,jun */
  31,28,31,30,31,30,
  /* july,aug,sep,oct,nov,dec */
  31,31,30,31,30,31
};

/*
 * basic time calculation functionality for localtime and gmtime
 * setups up prtm argument with correct values based upon input number
 * of seconds.
 */
static void
PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
{
    /* convert tsecs back to year,month,day,hour,secs */
    JSInt32 year    = 0;
    JSInt32 month   = 0;
    JSInt32 yday    = 0;
    JSInt32 mday    = 0;
    JSInt32 wday    = 6; /* start on a Sunday */
    JSInt32 days    = 0;
    JSInt32 seconds = 0;
    JSInt32 minutes = 0;
    JSInt32 hours   = 0;
    JSInt32 isleap  = 0;
    JSInt64 result;
    JSInt64	result1;
    JSInt64	result2;
    JSInt64 base;

    JSLL_UI2L(result,0);
    JSLL_UI2L(result1,0);
    JSLL_UI2L(result2,0);

    /* get the base time via UTC */
    base = PRMJ_ToExtendedTime(0);
    JSLL_UI2L(result,  PRMJ_USEC_PER_SEC);
    JSLL_DIV(base,base,result);
    JSLL_ADD(tsecs,tsecs,base);

    JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
    JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
    JSLL_ADD(result2,result,result1);

  /* get the year */
    while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) {
	/* subtract a year from tsecs */
	JSLL_SUB(tsecs,tsecs,result);
	days += 365;
	/* is it a leap year ? */
	if(IS_LEAP(year)){
	    JSLL_SUB(tsecs,tsecs,result1);
	    days++;
	}
	year++;
	isleap = IS_LEAP(year);
    }

    JSLL_UI2L(result1,PRMJ_DAY_SECONDS);

    JSLL_DIV(result,tsecs,result1);
    JSLL_L2I(mday,result);

  /* let's find the month */
    while(((month == 1 && isleap) ?
	   (mday >= mtab[month] + 1) :
	   (mday >= mtab[month]))){
	yday += mtab[month];
	days += mtab[month];

	mday -= mtab[month];

    /* it's a Feb, check if this is a leap year */
	if(month == 1 && isleap != 0){
	    yday++;
	    days++;
	    mday--;
	}
	month++;
    }

    /* now adjust tsecs */
    JSLL_MUL(result,result,result1);
    JSLL_SUB(tsecs,tsecs,result);

    mday++; /* day of month always start with 1 */
    days += mday;
    wday = (days + wday) % 7;

    yday += mday;

    /* get the hours */
    JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
    JSLL_DIV(result,tsecs,result1);
    JSLL_L2I(hours,result);
    JSLL_MUL(result,result,result1);
    JSLL_SUB(tsecs,tsecs,result);

    /* get minutes */
    JSLL_UI2L(result1,60);
    JSLL_DIV(result,tsecs,result1);
    JSLL_L2I(minutes,result);
    JSLL_MUL(result,result,result1);
    JSLL_SUB(tsecs,tsecs,result);

    JSLL_L2I(seconds,tsecs);

    prtm->tm_usec  = 0L;
    prtm->tm_sec   = (JSInt8)seconds;
    prtm->tm_min   = (JSInt8)minutes;
    prtm->tm_hour  = (JSInt8)hours;
    prtm->tm_mday  = (JSInt8)mday;
    prtm->tm_mon   = (JSInt8)month;
    prtm->tm_wday  = (JSInt8)wday;
    prtm->tm_year  = (JSInt16)year;
    prtm->tm_yday  = (JSInt16)yday;
}

**** End of prmjtime.c. ****

**** Start of prmjtime.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#ifndef prmjtime_h___
#define prmjtime_h___
/*
 * PR date stuff for mocha and java. Placed here temporarily not to break
 * Navigator and localize changes to mocha.
 */
#include <time.h>
#include "jslong.h"
#ifdef MOZILLA_CLIENT
#include "jscompat.h"
#endif

JS_BEGIN_EXTERN_C

typedef struct PRMJTime       PRMJTime;

/*
 * Broken down form of 64 bit time value.
 */
struct PRMJTime {
    JSInt32 tm_usec;		/* microseconds of second (0-999999) */
    JSInt8 tm_sec;		/* seconds of minute (0-59) */
    JSInt8 tm_min;		/* minutes of hour (0-59) */
    JSInt8 tm_hour;		/* hour of day (0-23) */
    JSInt8 tm_mday;		/* day of month (1-31) */
    JSInt8 tm_mon;		/* month of year (0-11) */
    JSInt8 tm_wday;		/* 0=sunday, 1=monday, ... */
    JSInt16 tm_year;		/* absolute year, AD */
    JSInt16 tm_yday;		/* day of year (0 to 365) */
    JSInt8 tm_isdst;		/* non-zero if DST in effect */
};

/* Some handy constants */
#define PRMJ_USEC_PER_SEC	1000000L
#define PRMJ_USEC_PER_MSEC	1000L

/* Return the current local time in micro-seconds */
extern JSInt64
PRMJ_Now(void);

/* get the difference between this time zone and  gmt timezone in seconds */
extern time_t
PRMJ_LocalGMTDifference(void);

/* Format a time value into a buffer. Same semantics as strftime() */
extern size_t
PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm);

/* Get the DST offset for the local time passed in */
extern JSInt64
PRMJ_DSTOffset(JSInt64 time);

JS_END_EXTERN_C

#endif /* prmjtime_h___ */


**** End of prmjtime.h. ****

**** Start of resource.h. ****

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by js3240.rc
//

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

**** End of resource.h. ****

**** Start of e_acos.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_acos.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_acos(x)
 * Method :                  
 *	acos(x)  = pi/2 - asin(x)
 *	acos(-x) = pi/2 + asin(x)
 * For |x|<=0.5
 *	acos(x) = pi/2 - (x + x*x^2*R(x^2))	(see asin.c)
 * For x>0.5
 * 	acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2)))
 *		= 2asin(sqrt((1-x)/2))  
 *		= 2s + 2s*z*R(z) 	...z=(1-x)/2, s=sqrt(z)
 *		= 2f + (2c + 2s*z*R(z))
 *     where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term
 *     for f so that f+c ~ sqrt(z).
 * For x<-0.5
 *	acos(x) = pi - 2asin(sqrt((1-|x|)/2))
 *		= pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z)
 *
 * Special cases:
 *	if x is NaN, return x itself;
 *	if |x|>1, return NaN with invalid signal.
 *
 * Function needed: sqrt
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
one=  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
pi =  3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */
pio2_hi =  1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */
pio2_lo =  6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */
pS0 =  1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */
pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */
pS2 =  2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */
pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */
pS4 =  7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */
pS5 =  3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */
qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */
qS2 =  2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */
qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */
qS4 =  7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */

#ifdef __STDC__
	double __ieee754_acos(double x)
#else
	double __ieee754_acos(x)
	double x;
#endif
{
	double z,p,q,r,w,s,c,df;
	int hx,ix;
	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x3ff00000) {	/* |x| >= 1 */
	    if(((ix-0x3ff00000)|__LO(x))==0) {	/* |x|==1 */
		if(hx>0) return 0.0;		/* acos(1) = 0  */
		else return pi+2.0*pio2_lo;	/* acos(-1)= pi */
	    }
	    return (x-x)/(x-x);		/* acos(|x|>1) is NaN */
	}
	if(ix<0x3fe00000) {	/* |x| < 0.5 */
	    if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/
	    z = x*x;
	    p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
	    q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
	    r = p/q;
	    return pio2_hi - (x - (pio2_lo-x*r));
	} else  if (hx<0) {		/* x < -0.5 */
	    z = (one+x)*0.5;
	    p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
	    q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
	    s = fd_sqrt(z);
	    r = p/q;
	    w = r*s-pio2_lo;
	    return pi - 2.0*(s+w);
	} else {			/* x > 0.5 */
	    z = (one-x)*0.5;
	    s = fd_sqrt(z);
	    df = s;
	    __LO(df) = 0;
	    c  = (z-df*df)/(s+df);
	    p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))));
	    q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4)));
	    r = p/q;
	    w = r*s+c;
	    return 2.0*(df+w);
	}
}

**** End of e_acos.c. ****

**** Start of e_acosh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_acosh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_acosh(x)
 * Method :
 *	Based on 
 *		acosh(x) = log [ x + sqrt(x*x-1) ]
 *	we have
 *		acosh(x) := log(x)+ln2,	if x is large; else
 *		acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else
 *		acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1.
 *
 * Special cases:
 *	acosh(x) is NaN with signal if x<1.
 *	acosh(NaN) is NaN without signal.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
one	= 1.0,
ln2	= 6.93147180559945286227e-01;  /* 0x3FE62E42, 0xFEFA39EF */

#ifdef __STDC__
	double __ieee754_acosh(double x)
#else
	double __ieee754_acosh(x)
	double x;
#endif
{	
	double t;
	int hx;
	hx = __HI(x);
	if(hx<0x3ff00000) {		/* x < 1 */
	    return (x-x)/(x-x);
	} else if(hx >=0x41b00000) {	/* x > 2**28 */
	    if(hx >=0x7ff00000) {	/* x is inf of NaN */
	        return x+x;
	    } else 
		return __ieee754_log(x)+ln2;	/* acosh(huge)=log(2x) */
	} else if(((hx-0x3ff00000)|__LO(x))==0) {
	    return 0.0;			/* acosh(1) = 0 */
	} else if (hx > 0x40000000) {	/* 2**28 > x > 2 */
	    t=x*x;
	    return __ieee754_log(2.0*x-one/(x+fd_sqrt(t-one)));
	} else {			/* 1<x<2 */
	    t = x-one;
	    return fd_log1p(t+fd_sqrt(2.0*t+t*t));
	}
}

**** End of e_acosh.c. ****

**** Start of e_asin.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_asin.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_asin(x)
 * Method :                  
 *	Since  asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ...
 *	we approximate asin(x) on [0,0.5] by
 *		asin(x) = x + x*x^2*R(x^2)
 *	where
 *		R(x^2) is a rational approximation of (asin(x)-x)/x^3 
 *	and its remez error is bounded by
 *		|(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75)
 *
 *	For x in [0.5,1]
 *		asin(x) = pi/2-2*asin(sqrt((1-x)/2))
 *	Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2;
 *	then for x>0.98
 *		asin(x) = pi/2 - 2*(s+s*z*R(z))
 *			= pio2_hi - (2*(s+s*z*R(z)) - pio2_lo)
 *	For x<=0.98, let pio4_hi = pio2_hi/2, then
 *		f = hi part of s;
 *		c = sqrt(z) - f = (z-f*f)/(s+f) 	...f+c=sqrt(z)
 *	and
 *		asin(x) = pi/2 - 2*(s+s*z*R(z))
 *			= pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo)
 *			= pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c))
 *
 * Special cases:
 *	if x is NaN, return x itself;
 *	if |x|>1, return NaN with invalid signal.
 *
 */


#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
one =  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
huge =  1.000e+300,
pio2_hi =  1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */
pio2_lo =  6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */
pio4_hi =  7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */
	/* coefficient for R(x^2) */
pS0 =  1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */
pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */
pS2 =  2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */
pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */
pS4 =  7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */
pS5 =  3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */
qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */
qS2 =  2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */
qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */
qS4 =  7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */

#ifdef __STDC__
	double __ieee754_asin(double x)
#else
	double __ieee754_asin(x)
	double x;
#endif
{
	double t,w,p,q,c,r,s;
	int hx,ix;
	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>= 0x3ff00000) {		/* |x|>= 1 */
	    if(((ix-0x3ff00000)|__LO(x))==0)
		    /* asin(1)=+-pi/2 with inexact */
		return x*pio2_hi+x*pio2_lo;	
	    return (x-x)/(x-x);		/* asin(|x|>1) is NaN */   
	} else if (ix<0x3fe00000) {	/* |x|<0.5 */
	    if(ix<0x3e400000) {		/* if |x| < 2**-27 */
		if(huge+x>one) return x;/* return x with inexact if x!=0*/
	    } else 
		t = x*x;
		p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5)))));
		q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4)));
		w = p/q;
		return x+x*w;
	}
	/* 1> |x|>= 0.5 */
	w = one-fd_fabs(x);
	t = w*0.5;
	p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5)))));
	q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4)));
	s = fd_sqrt(t);
	if(ix>=0x3FEF3333) { 	/* if |x| > 0.975 */
	    w = p/q;
	    t = pio2_hi-(2.0*(s+s*w)-pio2_lo);
	} else {
	    w  = s;
	    __LO(w) = 0;
	    c  = (t-w*w)/(s+w);
	    r  = p/q;
	    p  = 2.0*s*r-(pio2_lo-2.0*c);
	    q  = pio4_hi-2.0*w;
	    t  = pio4_hi-(p-q);
	}    
	if(hx>0) return t; else return -t;    
}

**** End of e_asin.c. ****

**** Start of e_atan2.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_atan2.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_atan2(y,x)
 * Method :
 *	1. Reduce y to positive by atan2(y,x)=-atan2(-y,x).
 *	2. Reduce x to positive by (if x and y are unexceptional): 
 *		ARG (x+iy) = arctan(y/x)   	   ... if x > 0,
 *		ARG (x+iy) = pi - arctan[y/(-x)]   ... if x < 0,
 *
 * Special cases:
 *
 *	ATAN2((anything), NaN ) is NaN;
 *	ATAN2(NAN , (anything) ) is NaN;
 *	ATAN2(+-0, +(anything but NaN)) is +-0  ;
 *	ATAN2(+-0, -(anything but NaN)) is +-pi ;
 *	ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2;
 *	ATAN2(+-(anything but INF and NaN), +INF) is +-0 ;
 *	ATAN2(+-(anything but INF and NaN), -INF) is +-pi;
 *	ATAN2(+-INF,+INF ) is +-pi/4 ;
 *	ATAN2(+-INF,-INF ) is +-3pi/4;
 *	ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2;
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
tiny  = 1.0e-300,
zero  = 0.0,
pi_o_4  = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */
pi_o_2  = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */
pi      = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */
pi_lo   = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */

#ifdef __STDC__
	double __ieee754_atan2(double y, double x)
#else
	double __ieee754_atan2(y,x)
	double  y,x;
#endif
{  
	double z;
	int k,m,hx,hy,ix,iy;
	unsigned lx,ly;

	hx = __HI(x); ix = hx&0x7fffffff;
	lx = __LO(x);
	hy = __HI(y); iy = hy&0x7fffffff;
	ly = __LO(y);
	if(((ix|((lx|-(int)lx)>>31))>0x7ff00000)||
	   ((iy|((ly|-(int)ly)>>31))>0x7ff00000))	/* x or y is NaN */
	   return x+y;
	if((hx-0x3ff00000|lx)==0) return fd_atan(y);   /* x=1.0 */
	m = ((hy>>31)&1)|((hx>>30)&2);	/* 2*sign(x)+sign(y) */

    /* when y = 0 */
	if((iy|ly)==0) {
	    switch(m) {
		case 0: 
		case 1: return y; 	/* atan(+-0,+anything)=+-0 */
		case 2: return  pi+tiny;/* atan(+0,-anything) = pi */
		case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */
	    }
	}
    /* when x = 0 */
	if((ix|lx)==0) return (hy<0)?  -pi_o_2-tiny: pi_o_2+tiny;
	    
    /* when x is INF */
	if(ix==0x7ff00000) {
	    if(iy==0x7ff00000) {
		switch(m) {
		    case 0: return  pi_o_4+tiny;/* atan(+INF,+INF) */
		    case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */
		    case 2: return  3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/
		    case 3: return -3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/
		}
	    } else {
		switch(m) {
		    case 0: return  zero  ;	/* atan(+...,+INF) */
		    case 1: return -zero  ;	/* atan(-...,+INF) */
		    case 2: return  pi+tiny  ;	/* atan(+...,-INF) */
		    case 3: return -pi-tiny  ;	/* atan(-...,-INF) */
		}
	    }
	}
    /* when y is INF */
	if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny;

    /* compute y/x */
	k = (iy-ix)>>20;
	if(k > 60) z=pi_o_2+0.5*pi_lo; 	/* |y/x| >  2**60 */
	else if(hx<0&&k<-60) z=0.0; 	/* |y|/x < -2**60 */
	else z=fd_atan(fd_fabs(y/x));		/* safe to do y/x */
	switch (m) {
	    case 0: return       z  ;	/* atan(+,+) */
	    case 1: __HI(z) ^= 0x80000000;
		    return       z  ;	/* atan(-,+) */
	    case 2: return  pi-(z-pi_lo);/* atan(+,-) */
	    default: /* case 3 */
	    	    return  (z-pi_lo)-pi;/* atan(-,-) */
	}
}

**** End of e_atan2.c. ****

**** Start of e_atanh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_atanh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_atanh(x)
 * Method :
 *    1.Reduced x to positive by atanh(-x) = -atanh(x)
 *    2.For x>=0.5
 *                  1              2x                          x
 *	atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------)
 *                  2             1 - x                      1 - x
 *	
 * 	For x<0.5
 *	atanh(x) = 0.5*log1p(2x+2x*x/(1-x))
 *
 * Special cases:
 *	atanh(x) is NaN if |x| > 1 with signal;
 *	atanh(NaN) is that NaN with no signal;
 *	atanh(+-1) is +-INF with signal.
 *
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double one = 1.0, huge = 1e300;
#else
static double one = 1.0, huge = 1e300;
#endif

static double zero = 0.0;

#ifdef __STDC__
	double __ieee754_atanh(double x)
#else
	double __ieee754_atanh(x)
	double x;
#endif
{
	double t;
	int hx,ix;
	unsigned lx;
	hx = __HI(x);		/* high word */
	lx = __LO(x);		/* low word */
	ix = hx&0x7fffffff;
	if ((ix|((lx|(-(int)lx))>>31))>0x3ff00000) /* |x|>1 */
	    return (x-x)/(x-x);
	if(ix==0x3ff00000) 
	    return x/zero;
	if(ix<0x3e300000&&(huge+x)>zero) return x;	/* x<2**-28 */
	__HI(x) = ix;		/* x <- |x| */
	if(ix<0x3fe00000) {		/* x < 0.5 */
	    t = x+x;
	    t = 0.5*fd_log1p(t+t*x/(one-x));
	} else 
	    t = 0.5*fd_log1p((x+x)/(one-x));
	if(hx>=0) return t; else return -t;
}

**** End of e_atanh.c. ****

**** Start of e_cosh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_cosh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_cosh(x)
 * Method : 
 * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2
 *	1. Replace x by |x| (cosh(x) = cosh(-x)). 
 *	2. 
 *		                                        [ exp(x) - 1 ]^2 
 *	    0        <= x <= ln2/2  :  cosh(x) := 1 + -------------------
 *			       			           2*exp(x)
 *
 *		                                  exp(x) +  1/exp(x)
 *	    ln2/2    <= x <= 22     :  cosh(x) := -------------------
 *			       			          2
 *	    22       <= x <= lnovft :  cosh(x) := exp(x)/2 
 *	    lnovft   <= x <= ln2ovft:  cosh(x) := exp(x/2)/2 * exp(x/2)
 *	    ln2ovft  <  x	    :  cosh(x) := huge*huge (overflow)
 *
 * Special cases:
 *	cosh(x) is |x| if x is +INF, -INF, or NaN.
 *	only cosh(0)=1 is exact for finite x.
 */

#include "fdlibm.h"

#ifdef _WIN32
#define huge myhuge
#endif

#ifdef __STDC__
static const double one = 1.0, half=0.5, huge = 1.0e300;
#else
static double one = 1.0, half=0.5, huge = 1.0e300;
#endif

#ifdef __STDC__
	double __ieee754_cosh(double x)
#else
	double __ieee754_cosh(x)
	double x;
#endif
{	
	double t,w;
	int ix;
	unsigned lx;

    /* High word of |x|. */
	ix = __HI(x);
	ix &= 0x7fffffff;

    /* x is INF or NaN */
	if(ix>=0x7ff00000) return x*x;	

    /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */
	if(ix<0x3fd62e43) {
	    t = fd_expm1(fd_fabs(x));
	    w = one+t;
	    if (ix<0x3c800000) return w;	/* cosh(tiny) = 1 */
	    return one+(t*t)/(w+w);
	}

    /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */
	if (ix < 0x40360000) {
		t = __ieee754_exp(fd_fabs(x));
		return half*t+half/t;
	}

    /* |x| in [22, log(maxdouble)] return half*exp(|x|) */
	if (ix < 0x40862E42)  return half*__ieee754_exp(fd_fabs(x));

    /* |x| in [log(maxdouble), overflowthresold] */
	lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x);
	if (ix<0x408633CE || 
	      (ix==0x408633ce)&&(lx<=(unsigned)0x8fb9f87d)) {
	    w = __ieee754_exp(half*fd_fabs(x));
	    t = half*w;
	    return t*w;
	}

    /* |x| > overflowthresold, cosh(x) overflow */
	return huge*huge;
}

**** End of e_cosh.c. ****

**** Start of e_exp.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_exp.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_exp(x)
 * Returns the exponential of x.
 *
 * Method
 *   1. Argument reduction:
 *      Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658.
 *	Given x, find r and integer k such that
 *
 *               x = k*ln2 + r,  |r| <= 0.5*ln2.  
 *
 *      Here r will be represented as r = hi-lo for better 
 *	accuracy.
 *
 *   2. Approximation of exp(r) by a special rational function on
 *	the interval [0,0.34658]:
 *	Write
 *	    R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ...
 *      We use a special Reme algorithm on [0,0.34658] to generate 
 * 	a polynomial of degree 5 to approximate R. The maximum error 
 *	of this polynomial approximation is bounded by 2**-59. In
 *	other words,
 *	    R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5
 *  	(where z=r*r, and the values of P1 to P5 are listed below)
 *	and
 *	    |                  5          |     -59
 *	    | 2.0+P1*z+...+P5*z   -  R(z) | <= 2 
 *	    |                             |
 *	The computation of exp(r) thus becomes
 *                             2*r
 *		exp(r) = 1 + -------
 *		              R - r
 *                                 r*R1(r)	
 *		       = 1 + r + ----------- (for better accuracy)
 *		                  2 - R1(r)
 *	where
 *			         2       4             10
 *		R1(r) = r - (P1*r  + P2*r  + ... + P5*r   ).
 *	
 *   3. Scale back to obtain exp(x):
 *	From step 1, we have
 *	   exp(x) = 2^k * exp(r)
 *
 * Special cases:
 *	exp(INF) is INF, exp(NaN) is NaN;
 *	exp(-INF) is 0, and
 *	for finite argument, only exp(0)=1 is exact.
 *
 * Accuracy:
 *	according to an error analysis, the error is always less than
 *	1 ulp (unit in the last place).
 *
 * Misc. info.
 *	For IEEE double 
 *	    if x >  7.09782712893383973096e+02 then exp(x) overflow
 *	    if x < -7.45133219101941108420e+02 then exp(x) underflow
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
one	= 1.0,
halF[2]	= {0.5,-0.5,},
huge	= 1.0e+300,
twom1000= 9.33263618503218878990e-302,     /* 2**-1000=0x01700000,0*/
o_threshold=  7.09782712893383973096e+02,  /* 0x40862E42, 0xFEFA39EF */
u_threshold= -7.45133219101941108420e+02,  /* 0xc0874910, 0xD52D3051 */
ln2HI[2]   ={ 6.93147180369123816490e-01,  /* 0x3fe62e42, 0xfee00000 */
	     -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */
ln2LO[2]   ={ 1.90821492927058770002e-10,  /* 0x3dea39ef, 0x35793c76 */
	     -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */
invln2 =  1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */
P1   =  1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */
P2   = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */
P3   =  6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */
P4   = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */
P5   =  4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */


#ifdef __STDC__
	double __ieee754_exp(double x)	/* default IEEE double exp */
#else
	double __ieee754_exp(x)	/* default IEEE double exp */
	double x;
#endif
{
	double y,hi,lo,c,t;
	int k,xsb;
	unsigned hx;

	hx  = __HI(x);	/* high word of x */
	xsb = (hx>>31)&1;		/* sign bit of x */
	hx &= 0x7fffffff;		/* high word of |x| */

    /* filter out non-finite argument */
	if(hx >= 0x40862E42) {			/* if |x|>=709.78... */
            if(hx>=0x7ff00000) {
		if(((hx&0xfffff)|__LO(x))!=0) 
		     return x+x; 		/* NaN */
		else return (xsb==0)? x:0.0;	/* exp(+-inf)={inf,0} */
	    }
	    if(x > o_threshold) return huge*huge; /* overflow */
	    if(x < u_threshold) return twom1000*twom1000; /* underflow */
	}

    /* argument reduction */
	if(hx > 0x3fd62e42) {		/* if  |x| > 0.5 ln2 */ 
	    if(hx < 0x3FF0A2B2) {	/* and |x| < 1.5 ln2 */
		hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb;
	    } else {
		k  = (int)(invln2*x+halF[xsb]);
		t  = k;
		hi = x - t*ln2HI[0];	/* t*ln2HI is exact here */
		lo = t*ln2LO[0];
	    }
	    x  = hi - lo;
	} 
	else if(hx < 0x3e300000)  {	/* when |x|<2**-28 */
	    if(huge+x>one) return one+x;/* trigger inexact */
	}
	else k = 0;

    /* x is now in primary range */
	t  = x*x;
	c  = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))));
	if(k==0) 	return one-((x*c)/(c-2.0)-x); 
	else 		y = one-((lo-(x*c)/(2.0-c))-hi);
	if(k >= -1021) {
	    __HI(y) += (k<<20);	/* add k to y's exponent */
	    return y;
	} else {
	    __HI(y) += ((k+1000)<<20);/* add k to y's exponent */
	    return y*twom1000;
	}
}

**** End of e_exp.c. ****

**** Start of e_fmod.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_fmod.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * __ieee754_fmod(x,y)
 * Return x mod y in exact arithmetic
 * Method: shift and subtract
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double one = 1.0, Zero[] = {0.0, -0.0,};
#else
static double one = 1.0, Zero[] = {0.0, -0.0,};
#endif

#ifdef __STDC__
	double __ieee754_fmod(double x, double y)
#else
	double __ieee754_fmod(x,y)
	double x,y ;
#endif
{
	int n,hx,hy,hz,ix,iy,sx,i;
	unsigned lx,ly,lz;

	hx = __HI(x);		/* high word of x */
	lx = __LO(x);		/* low  word of x */
	hy = __HI(y);		/* high word of y */
	ly = __LO(y);		/* low  word of y */
	sx = hx&0x80000000;		/* sign of x */
	hx ^=sx;		/* |x| */
	hy &= 0x7fffffff;	/* |y| */

    /* purge off exception values */
	if((hy|ly)==0||(hx>=0x7ff00000)||	/* y=0,or x not finite */
	  ((hy|((ly|-(int)ly)>>31))>0x7ff00000))	/* or y is NaN */
	    return (x*y)/(x*y);
	if(hx<=hy) {
	    if((hx<hy)||(lx<ly)) return x;	/* |x|<|y| return x */
	    if(lx==ly) 
		return Zero[(unsigned)sx>>31];	/* |x|=|y| return x*0*/
	}

    /* determine ix = ilogb(x) */
	if(hx<0x00100000) {	/* subnormal x */
	    if(hx==0) {
		for (ix = -1043, i=lx; i>0; i<<=1) ix -=1;
	    } else {
		for (ix = -1022,i=(hx<<11); i>0; i<<=1) ix -=1;
	    }
	} else ix = (hx>>20)-1023;

    /* determine iy = ilogb(y) */
	if(hy<0x00100000) {	/* subnormal y */
	    if(hy==0) {
		for (iy = -1043, i=ly; i>0; i<<=1) iy -=1;
	    } else {
		for (iy = -1022,i=(hy<<11); i>0; i<<=1) iy -=1;
	    }
	} else iy = (hy>>20)-1023;

    /* set up {hx,lx}, {hy,ly} and align y to x */
	if(ix >= -1022) 
	    hx = 0x00100000|(0x000fffff&hx);
	else {		/* subnormal x, shift x to normal */
	    n = -1022-ix;
	    if(n<=31) {
	        hx = (hx<<n)|(lx>>(32-n));
	        lx <<= n;
	    } else {
		hx = lx<<(n-32);
		lx = 0;
	    }
	}
	if(iy >= -1022) 
	    hy = 0x00100000|(0x000fffff&hy);
	else {		/* subnormal y, shift y to normal */
	    n = -1022-iy;
	    if(n<=31) {
	        hy = (hy<<n)|(ly>>(32-n));
	        ly <<= n;
	    } else {
		hy = ly<<(n-32);
		ly = 0;
	    }
	}

    /* fix point fmod */
	n = ix - iy;
	while(n--) {
	    hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1;
	    if(hz<0){hx = hx+hx+(lx>>31); lx = lx+lx;}
	    else {
	    	if((hz|lz)==0) 		/* return sign(x)*0 */
		    return Zero[(unsigned)sx>>31];
	    	hx = hz+hz+(lz>>31); lx = lz+lz;
	    }
	}
	hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1;
	if(hz>=0) {hx=hz;lx=lz;}

    /* convert back to floating value and restore the sign */
	if((hx|lx)==0) 			/* return sign(x)*0 */
	    return Zero[(unsigned)sx>>31];	
	while(hx<0x00100000) {		/* normalize x */
	    hx = hx+hx+(lx>>31); lx = lx+lx;
	    iy -= 1;
	}
	if(iy>= -1022) {	/* normalize output */
	    hx = ((hx-0x00100000)|((iy+1023)<<20));
	    __HI(x) = hx|sx;
	    __LO(x) = lx;
	} else {		/* subnormal output */
	    n = -1022 - iy;
	    if(n<=20) {
		lx = (lx>>n)|((unsigned)hx<<(32-n));
		hx >>= n;
	    } else if (n<=31) {
		lx = (hx<<(32-n))|(lx>>n); hx = sx;
	    } else {
		lx = hx>>(n-32); hx = sx;
	    }
	    __HI(x) = hx|sx;
	    __LO(x) = lx;
	    x *= one;		/* create necessary signal */
	}
	return x;		/* exact output */
}

**** End of e_fmod.c. ****

**** Start of e_gamma.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_gamma.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_gamma(x)
 * Return the logarithm of the Gamma function of x.
 *
 * Method: call __ieee754_gamma_r
 */

#include "fdlibm.h"

extern int signgam;

#ifdef __STDC__
	double __ieee754_gamma(double x)
#else
	double __ieee754_gamma(x)
	double x;
#endif
{
	return __ieee754_gamma_r(x,&signgam);
}

**** End of e_gamma.c. ****

**** Start of e_gamma_r.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_gamma_r.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_gamma_r(x, signgamp)
 * Reentrant version of the logarithm of the Gamma function 
 * with user provide pointer for the sign of Gamma(x). 
 *
 * Method: See __ieee754_lgamma_r
 */

#include "fdlibm.h"

#ifdef __STDC__
	double __ieee754_gamma_r(double x, int *signgamp)
#else
	double __ieee754_gamma_r(x,signgamp)
	double x; int *signgamp;
#endif
{
	return __ieee754_lgamma_r(x,signgamp);
}

**** End of e_gamma_r.c. ****

**** Start of e_hypot.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_hypot.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_hypot(x,y)
 *
 * Method :                  
 *	If (assume round-to-nearest) z=x*x+y*y 
 *	has error less than sqrt(2)/2 ulp, than 
 *	sqrt(z) has error less than 1 ulp (exercise).
 *
 *	So, compute sqrt(x*x+y*y) with some care as 
 *	follows to get the error below 1 ulp:
 *
 *	Assume x>y>0;
 *	(if possible, set rounding to round-to-nearest)
 *	1. if x > 2y  use
 *		x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y
 *	where x1 = x with lower 32 bits cleared, x2 = x-x1; else
 *	2. if x <= 2y use
 *		t1*y1+((x-y)*(x-y)+(t1*y2+t2*y))
 *	where t1 = 2x with lower 32 bits cleared, t2 = 2x-t1, 
 *	y1= y with lower 32 bits chopped, y2 = y-y1.
 *		
 *	NOTE: scaling may be necessary if some argument is too 
 *	      large or too tiny
 *
 * Special cases:
 *	hypot(x,y) is INF if x or y is +INF or -INF; else
 *	hypot(x,y) is NAN if x or y is NAN.
 *
 * Accuracy:
 * 	hypot(x,y) returns sqrt(x^2+y^2) with error less 
 * 	than 1 ulps (units in the last place) 
 */

#include "fdlibm.h"

#ifdef __STDC__
	double __ieee754_hypot(double x, double y)
#else
	double __ieee754_hypot(x,y)
	double x, y;
#endif
{
	double a=x,b=y,t1,t2,y1,y2,w;
	int j,k,ha,hb;

	ha = __HI(x)&0x7fffffff;	/* high word of  x */
	hb = __HI(y)&0x7fffffff;	/* high word of  y */
	if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;}
	__HI(a) = ha;	/* a <- |a| */
	__HI(b) = hb;	/* b <- |b| */
	if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */
	k=0;
	if(ha > 0x5f300000) {	/* a>2**500 */
	   if(ha >= 0x7ff00000) {	/* Inf or NaN */
	       w = a+b;			/* for sNaN */
	       if(((ha&0xfffff)|__LO(a))==0) w = a;
	       if(((hb^0x7ff00000)|__LO(b))==0) w = b;
	       return w;
	   }
	   /* scale a and b by 2**-600 */
	   ha -= 0x25800000; hb -= 0x25800000;	k += 600;
	   __HI(a) = ha;
	   __HI(b) = hb;
	}
	if(hb < 0x20b00000) {	/* b < 2**-500 */
	    if(hb <= 0x000fffff) {	/* subnormal b or 0 */	
		if((hb|(__LO(b)))==0) return a;
		t1=0;
		__HI(t1) = 0x7fd00000;	/* t1=2^1022 */
		b *= t1;
		a *= t1;
		k -= 1022;
	    } else {		/* scale a and b by 2^600 */
	        ha += 0x25800000; 	/* a *= 2^600 */
		hb += 0x25800000;	/* b *= 2^600 */
		k -= 600;
	   	__HI(a) = ha;
	   	__HI(b) = hb;
	    }
	}
    /* medium size a and b */
	w = a-b;
	if (w>b) {
	    t1 = 0;
	    __HI(t1) = ha;
	    t2 = a-t1;
	    w  = fd_sqrt(t1*t1-(b*(-b)-t2*(a+t1)));
	} else {
	    a  = a+a;
	    y1 = 0;
	    __HI(y1) = hb;
	    y2 = b - y1;
	    t1 = 0;
	    __HI(t1) = ha+0x00100000;
	    t2 = a - t1;
	    w  = fd_sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b)));
	}
	if(k!=0) {
	    t1 = 1.0;
	    __HI(t1) += (k<<20);
	    return t1*w;
	} else return w;
}

**** End of e_hypot.c. ****

**** Start of e_j0.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_j0.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_j0(x), __ieee754_y0(x)
 * Bessel function of the first and second kinds of order zero.
 * Method -- j0(x):
 *	1. For tiny x, we use j0(x) = 1 - x^2/4 + x^4/64 - ...
 *	2. Reduce x to |x| since j0(x)=j0(-x),  and
 *	   for x in (0,2)
 *		j0(x) = 1-z/4+ z^2*R0/S0,  where z = x*x;
 *	   (precision:  |j0-1+z/4-z^2R0/S0 |<2**-63.67 )
 *	   for x in (2,inf)
 * 		j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0))
 * 	   where x0 = x-pi/4. It is better to compute sin(x0),cos(x0)
 *	   as follow:
 *		cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4)
 *			= 1/sqrt(2) * (cos(x) + sin(x))
 *		sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4)
 *			= 1/sqrt(2) * (sin(x) - cos(x))
 * 	   (To avoid cancellation, use
 *		sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
 * 	    to compute the worse one.)
 *	   
 *	3 Special cases
 *		j0(nan)= nan
 *		j0(0) = 1
 *		j0(inf) = 0
 *		
 * Method -- y0(x):
 *	1. For x<2.
 *	   Since 
 *		y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...)
 *	   therefore y0(x)-2/pi*j0(x)*ln(x) is an even function.
 *	   We use the following function to approximate y0,
 *		y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2
 *	   where 
 *		U(z) = u00 + u01*z + ... + u06*z^6
 *		V(z) = 1  + v01*z + ... + v04*z^4
 *	   with absolute approximation error bounded by 2**-72.
 *	   Note: For tiny x, U/V = u0 and j0(x)~1, hence
 *		y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27)
 *	2. For x>=2.
 * 		y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0))
 * 	   where x0 = x-pi/4. It is better to compute sin(x0),cos(x0)
 *	   by the method mentioned above.
 *	3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0.
 */

#include "fdlibm.h"

#ifdef __STDC__
static double pzero(double), qzero(double);
#else
static double pzero(), qzero();
#endif

#ifdef __STDC__
static const double 
#else
static double 
#endif
huge 	= 1e300,
one	= 1.0,
invsqrtpi=  5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */
tpi      =  6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
 		/* R0/S0 on [0, 2.00] */
R02  =  1.56249999999999947958e-02, /* 0x3F8FFFFF, 0xFFFFFFFD */
R03  = -1.89979294238854721751e-04, /* 0xBF28E6A5, 0xB61AC6E9 */
R04  =  1.82954049532700665670e-06, /* 0x3EBEB1D1, 0x0C503919 */
R05  = -4.61832688532103189199e-09, /* 0xBE33D5E7, 0x73D63FCE */
S01  =  1.56191029464890010492e-02, /* 0x3F8FFCE8, 0x82C8C2A4 */
S02  =  1.16926784663337450260e-04, /* 0x3F1EA6D2, 0xDD57DBF4 */
S03  =  5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */
S04  =  1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */

static double zero = 0.0;

#ifdef __STDC__
	double __ieee754_j0(double x) 
#else
	double __ieee754_j0(x) 
	double x;
#endif
{
	double z, s,c,ss,cc,r,u,v;
	int hx,ix;

	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x7ff00000) return one/(x*x);
	x = fd_fabs(x);
	if(ix >= 0x40000000) {	/* |x| >= 2.0 */
		s = fd_sin(x);
		c = fd_cos(x);
		ss = s-c;
		cc = s+c;
		if(ix<0x7fe00000) {  /* make sure x+x not overflow */
		    z = -fd_cos(x+x);
		    if ((s*c)<zero) cc = z/ss;
		    else 	    ss = z/cc;
		}
	/*
	 * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x)
	 * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x)
	 */
		if(ix>0x48000000) z = (invsqrtpi*cc)/fd_sqrt(x);
		else {
		    u = pzero(x); v = qzero(x);
		    z = invsqrtpi*(u*cc-v*ss)/fd_sqrt(x);
		}
		return z;
	}
	if(ix<0x3f200000) {	/* |x| < 2**-13 */
	    if(huge+x>one) {	/* raise inexact if x != 0 */
	        if(ix<0x3e400000) return one;	/* |x|<2**-27 */
	        else 	      return one - 0.25*x*x;
	    }
	}
	z = x*x;
	r =  z*(R02+z*(R03+z*(R04+z*R05)));
	s =  one+z*(S01+z*(S02+z*(S03+z*S04)));
	if(ix < 0x3FF00000) {	/* |x| < 1.00 */
	    return one + z*(-0.25+(r/s));
	} else {
	    u = 0.5*x;
	    return((one+u)*(one-u)+z*(r/s));
	}
}

#ifdef __STDC__
static const double
#else
static double
#endif
u00  = -7.38042951086872317523e-02, /* 0xBFB2E4D6, 0x99CBD01F */
u01  =  1.76666452509181115538e-01, /* 0x3FC69D01, 0x9DE9E3FC */
u02  = -1.38185671945596898896e-02, /* 0xBF8C4CE8, 0xB16CFA97 */
u03  =  3.47453432093683650238e-04, /* 0x3F36C54D, 0x20B29B6B */
u04  = -3.81407053724364161125e-06, /* 0xBECFFEA7, 0x73D25CAD */
u05  =  1.95590137035022920206e-08, /* 0x3E550057, 0x3B4EABD4 */
u06  = -3.98205194132103398453e-11, /* 0xBDC5E43D, 0x693FB3C8 */
v01  =  1.27304834834123699328e-02, /* 0x3F8A1270, 0x91C9C71A */
v02  =  7.60068627350353253702e-05, /* 0x3F13ECBB, 0xF578C6C1 */
v03  =  2.59150851840457805467e-07, /* 0x3E91642D, 0x7FF202FD */
v04  =  4.41110311332675467403e-10; /* 0x3DFE5018, 0x3BD6D9EF */

#ifdef __STDC__
	double __ieee754_y0(double x) 
#else
	double __ieee754_y0(x) 
	double x;
#endif
{
	double z, s,c,ss,cc,u,v;
	int hx,ix,lx;

        hx = __HI(x);
        ix = 0x7fffffff&hx;
        lx = __LO(x);
    /* Y0(NaN) is NaN, y0(-inf) is Nan, y0(inf) is 0  */
	if(ix>=0x7ff00000) return  one/(x+x*x); 
        if((ix|lx)==0) return -one/zero;
        if(hx<0) return zero/zero;
        if(ix >= 0x40000000) {  /* |x| >= 2.0 */
        /* y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0))
         * where x0 = x-pi/4
         *      Better formula:
         *              cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4)
         *                      =  1/sqrt(2) * (sin(x) + cos(x))
         *              sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
         *                      =  1/sqrt(2) * (sin(x) - cos(x))
         * To avoid cancellation, use
         *              sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
         * to compute the worse one.
         */
                s = fd_sin(x);
                c = fd_cos(x);
                ss = s-c;
                cc = s+c;
	/*
	 * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x)
	 * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x)
	 */
                if(ix<0x7fe00000) {  /* make sure x+x not overflow */
                    z = -fd_cos(x+x);
                    if ((s*c)<zero) cc = z/ss;
                    else            ss = z/cc;
                }
                if(ix>0x48000000) z = (invsqrtpi*ss)/fd_sqrt(x);
                else {
                    u = pzero(x); v = qzero(x);
                    z = invsqrtpi*(u*ss+v*cc)/fd_sqrt(x);
                }
                return z;
	}
	if(ix<=0x3e400000) {	/* x < 2**-27 */
	    return(u00 + tpi*__ieee754_log(x));
	}
	z = x*x;
	u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06)))));
	v = one+z*(v01+z*(v02+z*(v03+z*v04)));
	return(u/v + tpi*(__ieee754_j0(x)*__ieee754_log(x)));
}

/* The asymptotic expansions of pzero is
 *	1 - 9/128 s^2 + 11025/98304 s^4 - ...,	where s = 1/x.
 * For x >= 2, We approximate pzero by
 * 	pzero(x) = 1 + (R/S)
 * where  R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10
 * 	  S = 1 + pS0*s^2 + ... + pS4*s^10
 * and
 *	| pzero(x)-1-R/S | <= 2  ** ( -60.26)
 */
#ifdef __STDC__
static const double pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#else
static double pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#endif
  0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
 -7.03124999999900357484e-02, /* 0xBFB1FFFF, 0xFFFFFD32 */
 -8.08167041275349795626e+00, /* 0xC02029D0, 0xB44FA779 */
 -2.57063105679704847262e+02, /* 0xC0701102, 0x7B19E863 */
 -2.48521641009428822144e+03, /* 0xC0A36A6E, 0xCD4DCAFC */
 -5.25304380490729545272e+03, /* 0xC0B4850B, 0x36CC643D */
};
#ifdef __STDC__
static const double pS8[5] = {
#else
static double pS8[5] = {
#endif
  1.16534364619668181717e+02, /* 0x405D2233, 0x07A96751 */
  3.83374475364121826715e+03, /* 0x40ADF37D, 0x50596938 */
  4.05978572648472545552e+04, /* 0x40E3D2BB, 0x6EB6B05F */
  1.16752972564375915681e+05, /* 0x40FC810F, 0x8F9FA9BD */
  4.76277284146730962675e+04, /* 0x40E74177, 0x4F2C49DC */
};

#ifdef __STDC__
static const double pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#else
static double pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#endif
 -1.14125464691894502584e-11, /* 0xBDA918B1, 0x47E495CC */
 -7.03124940873599280078e-02, /* 0xBFB1FFFF, 0xE69AFBC6 */
 -4.15961064470587782438e+00, /* 0xC010A370, 0xF90C6BBF */
 -6.76747652265167261021e+01, /* 0xC050EB2F, 0x5A7D1783 */
 -3.31231299649172967747e+02, /* 0xC074B3B3, 0x6742CC63 */
 -3.46433388365604912451e+02, /* 0xC075A6EF, 0x28A38BD7 */
};
#ifdef __STDC__
static const double pS5[5] = {
#else
static double pS5[5] = {
#endif
  6.07539382692300335975e+01, /* 0x404E6081, 0x0C98C5DE */
  1.05125230595704579173e+03, /* 0x40906D02, 0x5C7E2864 */
  5.97897094333855784498e+03, /* 0x40B75AF8, 0x8FBE1D60 */
  9.62544514357774460223e+03, /* 0x40C2CCB8, 0xFA76FA38 */
  2.40605815922939109441e+03, /* 0x40A2CC1D, 0xC70BE864 */
};

#ifdef __STDC__
static const double pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
#else
static double pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
#endif
 -2.54704601771951915620e-09, /* 0xBE25E103, 0x6FE1AA86 */
 -7.03119616381481654654e-02, /* 0xBFB1FFF6, 0xF7C0E24B */
 -2.40903221549529611423e+00, /* 0xC00345B2, 0xAEA48074 */
 -2.19659774734883086467e+01, /* 0xC035F74A, 0x4CB94E14 */
 -5.80791704701737572236e+01, /* 0xC04D0A22, 0x420A1A45 */
 -3.14479470594888503854e+01, /* 0xC03F72AC, 0xA892D80F */
};
#ifdef __STDC__
static const double pS3[5] = {
#else
static double pS3[5] = {
#endif
  3.58560338055209726349e+01, /* 0x4041ED92, 0x84077DD3 */
  3.61513983050303863820e+02, /* 0x40769839, 0x464A7C0E */
  1.19360783792111533330e+03, /* 0x4092A66E, 0x6D1061D6 */
  1.12799679856907414432e+03, /* 0x40919FFC, 0xB8C39B7E */
  1.73580930813335754692e+02, /* 0x4065B296, 0xFC379081 */
};

#ifdef __STDC__
static const double pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#else
static double pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#endif
 -8.87534333032526411254e-08, /* 0xBE77D316, 0xE927026D */
 -7.03030995483624743247e-02, /* 0xBFB1FF62, 0x495E1E42 */
 -1.45073846780952986357e+00, /* 0xBFF73639, 0x8A24A843 */
 -7.63569613823527770791e+00, /* 0xC01E8AF3, 0xEDAFA7F3 */
 -1.11931668860356747786e+01, /* 0xC02662E6, 0xC5246303 */
 -3.23364579351335335033e+00, /* 0xC009DE81, 0xAF8FE70F */
};
#ifdef __STDC__
static const double pS2[5] = {
#else
static double pS2[5] = {
#endif
  2.22202997532088808441e+01, /* 0x40363865, 0x908B5959 */
  1.36206794218215208048e+02, /* 0x4061069E, 0x0EE8878F */
  2.70470278658083486789e+02, /* 0x4070E786, 0x42EA079B */
  1.53875394208320329881e+02, /* 0x40633C03, 0x3AB6FAFF */
  1.46576176948256193810e+01, /* 0x402D50B3, 0x44391809 */
};

#ifdef __STDC__
	static double pzero(double x)
#else
	static double pzero(x)
	double x;
#endif
{
#ifdef __STDC__
	const double *p,*q;
#else
	double *p,*q;
#endif
	double z,r,s;
	int ix;
	ix = 0x7fffffff&__HI(x);
	if(ix>=0x40200000)     {p = pR8; q= pS8;}
	else if(ix>=0x40122E8B){p = pR5; q= pS5;}
	else if(ix>=0x4006DB6D){p = pR3; q= pS3;}
	else if(ix>=0x40000000){p = pR2; q= pS2;}
	z = one/(x*x);
	r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
	s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4]))));
	return one+ r/s;
}
		

/* For x >= 8, the asymptotic expansions of qzero is
 *	-1/8 s + 75/1024 s^3 - ..., where s = 1/x.
 * We approximate pzero by
 * 	qzero(x) = s*(-1.25 + (R/S))
 * where  R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10
 * 	  S = 1 + qS0*s^2 + ... + qS5*s^12
 * and
 *	| qzero(x)/s +1.25-R/S | <= 2  ** ( -61.22)
 */
#ifdef __STDC__
static const double qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#else
static double qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#endif
  0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
  7.32421874999935051953e-02, /* 0x3FB2BFFF, 0xFFFFFE2C */
  1.17682064682252693899e+01, /* 0x40278952, 0x5BB334D6 */
  5.57673380256401856059e+02, /* 0x40816D63, 0x15301825 */
  8.85919720756468632317e+03, /* 0x40C14D99, 0x3E18F46D */
  3.70146267776887834771e+04, /* 0x40E212D4, 0x0E901566 */
};
#ifdef __STDC__
static const double qS8[6] = {
#else
static double qS8[6] = {
#endif
  1.63776026895689824414e+02, /* 0x406478D5, 0x365B39BC */
  8.09834494656449805916e+03, /* 0x40BFA258, 0x4E6B0563 */
  1.42538291419120476348e+05, /* 0x41016652, 0x54D38C3F */
  8.03309257119514397345e+05, /* 0x412883DA, 0x83A52B43 */
  8.40501579819060512818e+05, /* 0x4129A66B, 0x28DE0B3D */
 -3.43899293537866615225e+05, /* 0xC114FD6D, 0x2C9530C5 */
};

#ifdef __STDC__
static const double qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#else
static double qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#endif
  1.84085963594515531381e-11, /* 0x3DB43D8F, 0x29CC8CD9 */
  7.32421766612684765896e-02, /* 0x3FB2BFFF, 0xD172B04C */
  5.83563508962056953777e+00, /* 0x401757B0, 0xB9953DD3 */
  1.35111577286449829671e+02, /* 0x4060E392, 0x0A8788E9 */
  1.02724376596164097464e+03, /* 0x40900CF9, 0x9DC8C481 */
  1.98997785864605384631e+03, /* 0x409F17E9, 0x53C6E3A6 */
};
#ifdef __STDC__
static const double qS5[6] = {
#else
static double qS5[6] = {
#endif
  8.27766102236537761883e+01, /* 0x4054B1B3, 0xFB5E1543 */
  2.07781416421392987104e+03, /* 0x40A03BA0, 0xDA21C0CE */
  1.88472887785718085070e+04, /* 0x40D267D2, 0x7B591E6D */
  5.67511122894947329769e+04, /* 0x40EBB5E3, 0x97E02372 */
  3.59767538425114471465e+04, /* 0x40E19118, 0x1F7A54A0 */
 -5.35434275601944773371e+03, /* 0xC0B4EA57, 0xBEDBC609 */
};

#ifdef __STDC__
static const double qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
#else
static double qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
#endif
  4.37741014089738620906e-09, /* 0x3E32CD03, 0x6ADECB82 */
  7.32411180042911447163e-02, /* 0x3FB2BFEE, 0x0E8D0842 */
  3.34423137516170720929e+00, /* 0x400AC0FC, 0x61149CF5 */
  4.26218440745412650017e+01, /* 0x40454F98, 0x962DAEDD */
  1.70808091340565596283e+02, /* 0x406559DB, 0xE25EFD1F */
  1.66733948696651168575e+02, /* 0x4064D77C, 0x81FA21E0 */
};
#ifdef __STDC__
static const double qS3[6] = {
#else
static double qS3[6] = {
#endif
  4.87588729724587182091e+01, /* 0x40486122, 0xBFE343A6 */
  7.09689221056606015736e+02, /* 0x40862D83, 0x86544EB3 */
  3.70414822620111362994e+03, /* 0x40ACF04B, 0xE44DFC63 */
  6.46042516752568917582e+03, /* 0x40B93C6C, 0xD7C76A28 */
  2.51633368920368957333e+03, /* 0x40A3A8AA, 0xD94FB1C0 */
 -1.49247451836156386662e+02, /* 0xC062A7EB, 0x201CF40F */
};

#ifdef __STDC__
static const double qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#else
static double qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#endif
  1.50444444886983272379e-07, /* 0x3E84313B, 0x54F76BDB */
  7.32234265963079278272e-02, /* 0x3FB2BEC5, 0x3E883E34 */
  1.99819174093815998816e+00, /* 0x3FFFF897, 0xE727779C */
  1.44956029347885735348e+01, /* 0x402CFDBF, 0xAAF96FE5 */
  3.16662317504781540833e+01, /* 0x403FAA8E, 0x29FBDC4A */
  1.62527075710929267416e+01, /* 0x403040B1, 0x71814BB4 */
};
#ifdef __STDC__
static const double qS2[6] = {
#else
static double qS2[6] = {
#endif
  3.03655848355219184498e+01, /* 0x403E5D96, 0xF7C07AED */
  2.69348118608049844624e+02, /* 0x4070D591, 0xE4D14B40 */
  8.44783757595320139444e+02, /* 0x408A6645, 0x22B3BF22 */
  8.82935845112488550512e+02, /* 0x408B977C, 0x9C5CC214 */
  2.12666388511798828631e+02, /* 0x406A9553, 0x0E001365 */
 -5.31095493882666946917e+00, /* 0xC0153E6A, 0xF8B32931 */
};

#ifdef __STDC__
	static double qzero(double x)
#else
	static double qzero(x)
	double x;
#endif
{
#ifdef __STDC__
	const double *p,*q;
#else
	double *p,*q;
#endif
	double s,r,z;
	int ix;
	ix = 0x7fffffff&__HI(x);
	if(ix>=0x40200000)     {p = qR8; q= qS8;}
	else if(ix>=0x40122E8B){p = qR5; q= qS5;}
	else if(ix>=0x4006DB6D){p = qR3; q= qS3;}
	else if(ix>=0x40000000){p = qR2; q= qS2;}
	z = one/(x*x);
	r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
	s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
	return (-.125 + r/s)/x;
}

**** End of e_j0.c. ****

**** Start of e_j1.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_j1.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_j1(x), __ieee754_y1(x)
 * Bessel function of the first and second kinds of order zero.
 * Method -- j1(x):
 *	1. For tiny x, we use j1(x) = x/2 - x^3/16 + x^5/384 - ...
 *	2. Reduce x to |x| since j1(x)=-j1(-x),  and
 *	   for x in (0,2)
 *		j1(x) = x/2 + x*z*R0/S0,  where z = x*x;
 *	   (precision:  |j1/x - 1/2 - R0/S0 |<2**-61.51 )
 *	   for x in (2,inf)
 * 		j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
 * 		y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1))
 * 	   where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1)
 *	   as follow:
 *		cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
 *			=  1/sqrt(2) * (sin(x) - cos(x))
 *		sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
 *			= -1/sqrt(2) * (sin(x) + cos(x))
 * 	   (To avoid cancellation, use
 *		sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
 * 	    to compute the worse one.)
 *	   
 *	3 Special cases
 *		j1(nan)= nan
 *		j1(0) = 0
 *		j1(inf) = 0
 *		
 * Method -- y1(x):
 *	1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN 
 *	2. For x<2.
 *	   Since 
 *		y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x^3-...)
 *	   therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function.
 *	   We use the following function to approximate y1,
 *		y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x^2
 *	   where for x in [0,2] (abs err less than 2**-65.89)
 *		U(z) = U0[0] + U0[1]*z + ... + U0[4]*z^4
 *		V(z) = 1  + v0[0]*z + ... + v0[4]*z^5
 *	   Note: For tiny x, 1/x dominate y1 and hence
 *		y1(tiny) = -2/pi/tiny, (choose tiny<2**-54)
 *	3. For x>=2.
 * 		y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1))
 * 	   where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1)
 *	   by method mentioned above.
 */

#include "fdlibm.h"

#ifdef __STDC__
static double pone(double), qone(double);
#else
static double pone(), qone();
#endif

#ifdef __STDC__
static const double 
#else
static double 
#endif
huge    = 1e300,
one	= 1.0,
invsqrtpi=  5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */
tpi      =  6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
	/* R0/S0 on [0,2] */
r00  = -6.25000000000000000000e-02, /* 0xBFB00000, 0x00000000 */
r01  =  1.40705666955189706048e-03, /* 0x3F570D9F, 0x98472C61 */
r02  = -1.59955631084035597520e-05, /* 0xBEF0C5C6, 0xBA169668 */
r03  =  4.96727999609584448412e-08, /* 0x3E6AAAFA, 0x46CA0BD9 */
s01  =  1.91537599538363460805e-02, /* 0x3F939D0B, 0x12637E53 */
s02  =  1.85946785588630915560e-04, /* 0x3F285F56, 0xB9CDF664 */
s03  =  1.17718464042623683263e-06, /* 0x3EB3BFF8, 0x333F8498 */
s04  =  5.04636257076217042715e-09, /* 0x3E35AC88, 0xC97DFF2C */
s05  =  1.23542274426137913908e-11; /* 0x3DAB2ACF, 0xCFB97ED8 */

static double zero    = 0.0;

#ifdef __STDC__
	double __ieee754_j1(double x) 
#else
	double __ieee754_j1(x) 
	double x;
#endif
{
	double z, s,c,ss,cc,r,u,v,y;
	int hx,ix;

	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x7ff00000) return one/x;
	y = fd_fabs(x);
	if(ix >= 0x40000000) {	/* |x| >= 2.0 */
		s = fd_sin(y);
		c = fd_cos(y);
		ss = -s-c;
		cc = s-c;
		if(ix<0x7fe00000) {  /* make sure y+y not overflow */
		    z = fd_cos(y+y);
		    if ((s*c)>zero) cc = z/ss;
		    else 	    ss = z/cc;
		}
	/*
	 * j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x)
	 * y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x)
	 */
		if(ix>0x48000000) z = (invsqrtpi*cc)/fd_sqrt(y);
		else {
		    u = pone(y); v = qone(y);
		    z = invsqrtpi*(u*cc-v*ss)/fd_sqrt(y);
		}
		if(hx<0) return -z;
		else  	 return  z;
	}
	if(ix<0x3e400000) {	/* |x|<2**-27 */
	    if(huge+x>one) return 0.5*x;/* inexact if x!=0 necessary */
	}
	z = x*x;
	r =  z*(r00+z*(r01+z*(r02+z*r03)));
	s =  one+z*(s01+z*(s02+z*(s03+z*(s04+z*s05))));
	r *= x;
	return(x*0.5+r/s);
}

#ifdef __STDC__
static const double U0[5] = {
#else
static double U0[5] = {
#endif
 -1.96057090646238940668e-01, /* 0xBFC91866, 0x143CBC8A */
  5.04438716639811282616e-02, /* 0x3FA9D3C7, 0x76292CD1 */
 -1.91256895875763547298e-03, /* 0xBF5F55E5, 0x4844F50F */
  2.35252600561610495928e-05, /* 0x3EF8AB03, 0x8FA6B88E */
 -9.19099158039878874504e-08, /* 0xBE78AC00, 0x569105B8 */
};
#ifdef __STDC__
static const double V0[5] = {
#else
static double V0[5] = {
#endif
  1.99167318236649903973e-02, /* 0x3F94650D, 0x3F4DA9F0 */
  2.02552581025135171496e-04, /* 0x3F2A8C89, 0x6C257764 */
  1.35608801097516229404e-06, /* 0x3EB6C05A, 0x894E8CA6 */
  6.22741452364621501295e-09, /* 0x3E3ABF1D, 0x5BA69A86 */
  1.66559246207992079114e-11, /* 0x3DB25039, 0xDACA772A */
};

#ifdef __STDC__
	double __ieee754_y1(double x) 
#else
	double __ieee754_y1(x) 
	double x;
#endif
{
	double z, s,c,ss,cc,u,v;
	int hx,ix,lx;

        hx = __HI(x);
        ix = 0x7fffffff&hx;
        lx = __LO(x);
    /* if Y1(NaN) is NaN, Y1(-inf) is NaN, Y1(inf) is 0 */
	if(ix>=0x7ff00000) return  one/(x+x*x); 
        if((ix|lx)==0) return -one/zero;
        if(hx<0) return zero/zero;
        if(ix >= 0x40000000) {  /* |x| >= 2.0 */
                s = fd_sin(x);
                c = fd_cos(x);
                ss = -s-c;
                cc = s-c;
                if(ix<0x7fe00000) {  /* make sure x+x not overflow */
                    z = fd_cos(x+x);
                    if ((s*c)>zero) cc = z/ss;
                    else            ss = z/cc;
                }
        /* y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0))
         * where x0 = x-3pi/4
         *      Better formula:
         *              cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
         *                      =  1/sqrt(2) * (sin(x) - cos(x))
         *              sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
         *                      = -1/sqrt(2) * (cos(x) + sin(x))
         * To avoid cancellation, use
         *              sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x))
         * to compute the worse one.
         */
                if(ix>0x48000000) z = (invsqrtpi*ss)/fd_sqrt(x);
                else {
                    u = pone(x); v = qone(x);
                    z = invsqrtpi*(u*ss+v*cc)/fd_sqrt(x);
                }
                return z;
        } 
        if(ix<=0x3c900000) {    /* x < 2**-54 */
            return(-tpi/x);
        } 
        z = x*x;
        u = U0[0]+z*(U0[1]+z*(U0[2]+z*(U0[3]+z*U0[4])));
        v = one+z*(V0[0]+z*(V0[1]+z*(V0[2]+z*(V0[3]+z*V0[4]))));
        return(x*(u/v) + tpi*(__ieee754_j1(x)*__ieee754_log(x)-one/x));
}

/* For x >= 8, the asymptotic expansions of pone is
 *	1 + 15/128 s^2 - 4725/2^15 s^4 - ...,	where s = 1/x.
 * We approximate pone by
 * 	pone(x) = 1 + (R/S)
 * where  R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10
 * 	  S = 1 + ps0*s^2 + ... + ps4*s^10
 * and
 *	| pone(x)-1-R/S | <= 2  ** ( -60.06)
 */

#ifdef __STDC__
static const double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#else
static double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#endif
  0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
  1.17187499999988647970e-01, /* 0x3FBDFFFF, 0xFFFFFCCE */
  1.32394806593073575129e+01, /* 0x402A7A9D, 0x357F7FCE */
  4.12051854307378562225e+02, /* 0x4079C0D4, 0x652EA590 */
  3.87474538913960532227e+03, /* 0x40AE457D, 0xA3A532CC */
  7.91447954031891731574e+03, /* 0x40BEEA7A, 0xC32782DD */
};
#ifdef __STDC__
static const double ps8[5] = {
#else
static double ps8[5] = {
#endif
  1.14207370375678408436e+02, /* 0x405C8D45, 0x8E656CAC */
  3.65093083420853463394e+03, /* 0x40AC85DC, 0x964D274F */
  3.69562060269033463555e+04, /* 0x40E20B86, 0x97C5BB7F */
  9.76027935934950801311e+04, /* 0x40F7D42C, 0xB28F17BB */
  3.08042720627888811578e+04, /* 0x40DE1511, 0x697A0B2D */
};

#ifdef __STDC__
static const double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#else
static double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#endif
  1.31990519556243522749e-11, /* 0x3DAD0667, 0xDAE1CA7D */
  1.17187493190614097638e-01, /* 0x3FBDFFFF, 0xE2C10043 */
  6.80275127868432871736e+00, /* 0x401B3604, 0x6E6315E3 */
  1.08308182990189109773e+02, /* 0x405B13B9, 0x452602ED */
  5.17636139533199752805e+02, /* 0x40802D16, 0xD052D649 */
  5.28715201363337541807e+02, /* 0x408085B8, 0xBB7E0CB7 */
};
#ifdef __STDC__
static const double ps5[5] = {
#else
static double ps5[5] = {
#endif
  5.92805987221131331921e+01, /* 0x404DA3EA, 0xA8AF633D */
  9.91401418733614377743e+02, /* 0x408EFB36, 0x1B066701 */
  5.35326695291487976647e+03, /* 0x40B4E944, 0x5706B6FB */
  7.84469031749551231769e+03, /* 0x40BEA4B0, 0xB8A5BB15 */
  1.50404688810361062679e+03, /* 0x40978030, 0x036F5E51 */
};

#ifdef __STDC__
static const double pr3[6] = {
#else
static double pr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
#endif
  3.02503916137373618024e-09, /* 0x3E29FC21, 0xA7AD9EDD */
  1.17186865567253592491e-01, /* 0x3FBDFFF5, 0x5B21D17B */
  3.93297750033315640650e+00, /* 0x400F76BC, 0xE85EAD8A */
  3.51194035591636932736e+01, /* 0x40418F48, 0x9DA6D129 */
  9.10550110750781271918e+01, /* 0x4056C385, 0x4D2C1837 */
  4.85590685197364919645e+01, /* 0x4048478F, 0x8EA83EE5 */
};
#ifdef __STDC__
static const double ps3[5] = {
#else
static double ps3[5] = {
#endif
  3.47913095001251519989e+01, /* 0x40416549, 0xA134069C */
  3.36762458747825746741e+02, /* 0x40750C33, 0x07F1A75F */
  1.04687139975775130551e+03, /* 0x40905B7C, 0x5037D523 */
  8.90811346398256432622e+02, /* 0x408BD67D, 0xA32E31E9 */
  1.03787932439639277504e+02, /* 0x4059F26D, 0x7C2EED53 */
};

#ifdef __STDC__
static const double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#else
static double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#endif
  1.07710830106873743082e-07, /* 0x3E7CE9D4, 0xF65544F4 */
  1.17176219462683348094e-01, /* 0x3FBDFF42, 0xBE760D83 */
  2.36851496667608785174e+00, /* 0x4002F2B7, 0xF98FAEC0 */
  1.22426109148261232917e+01, /* 0x40287C37, 0x7F71A964 */
  1.76939711271687727390e+01, /* 0x4031B1A8, 0x177F8EE2 */
  5.07352312588818499250e+00, /* 0x40144B49, 0xA574C1FE */
};
#ifdef __STDC__
static const double ps2[5] = {
#else
static double ps2[5] = {
#endif
  2.14364859363821409488e+01, /* 0x40356FBD, 0x8AD5ECDC */
  1.25290227168402751090e+02, /* 0x405F5293, 0x14F92CD5 */
  2.32276469057162813669e+02, /* 0x406D08D8, 0xD5A2DBD9 */
  1.17679373287147100768e+02, /* 0x405D6B7A, 0xDA1884A9 */
  8.36463893371618283368e+00, /* 0x4020BAB1, 0xF44E5192 */
};

#ifdef __STDC__
	static double pone(double x)
#else
	static double pone(x)
	double x;
#endif
{
#ifdef __STDC__
	const double *p,*q;
#else
	double *p,*q;
#endif
	double z,r,s;
        int ix;
        ix = 0x7fffffff&__HI(x);
        if(ix>=0x40200000)     {p = pr8; q= ps8;}
        else if(ix>=0x40122E8B){p = pr5; q= ps5;}
        else if(ix>=0x4006DB6D){p = pr3; q= ps3;}
        else if(ix>=0x40000000){p = pr2; q= ps2;}
        z = one/(x*x);
        r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
        s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4]))));
        return one+ r/s;
}
		

/* For x >= 8, the asymptotic expansions of qone is
 *	3/8 s - 105/1024 s^3 - ..., where s = 1/x.
 * We approximate pone by
 * 	qone(x) = s*(0.375 + (R/S))
 * where  R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10
 * 	  S = 1 + qs1*s^2 + ... + qs6*s^12
 * and
 *	| qone(x)/s -0.375-R/S | <= 2  ** ( -61.13)
 */

#ifdef __STDC__
static const double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#else
static double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */
#endif
  0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
 -1.02539062499992714161e-01, /* 0xBFBA3FFF, 0xFFFFFDF3 */
 -1.62717534544589987888e+01, /* 0xC0304591, 0xA26779F7 */
 -7.59601722513950107896e+02, /* 0xC087BCD0, 0x53E4B576 */
 -1.18498066702429587167e+04, /* 0xC0C724E7, 0x40F87415 */
 -4.84385124285750353010e+04, /* 0xC0E7A6D0, 0x65D09C6A */
};
#ifdef __STDC__
static const double qs8[6] = {
#else
static double qs8[6] = {
#endif
  1.61395369700722909556e+02, /* 0x40642CA6, 0xDE5BCDE5 */
  7.82538599923348465381e+03, /* 0x40BE9162, 0xD0D88419 */
  1.33875336287249578163e+05, /* 0x4100579A, 0xB0B75E98 */
  7.19657723683240939863e+05, /* 0x4125F653, 0x72869C19 */
  6.66601232617776375264e+05, /* 0x412457D2, 0x7719AD5C */
 -2.94490264303834643215e+05, /* 0xC111F969, 0x0EA5AA18 */
};

#ifdef __STDC__
static const double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#else
static double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */
#endif
 -2.08979931141764104297e-11, /* 0xBDB6FA43, 0x1AA1A098 */
 -1.02539050241375426231e-01, /* 0xBFBA3FFF, 0xCB597FEF */
 -8.05644828123936029840e+00, /* 0xC0201CE6, 0xCA03AD4B */
 -1.83669607474888380239e+02, /* 0xC066F56D, 0x6CA7B9B0 */
 -1.37319376065508163265e+03, /* 0xC09574C6, 0x6931734F */
 -2.61244440453215656817e+03, /* 0xC0A468E3, 0x88FDA79D */
};
#ifdef __STDC__
static const double qs5[6] = {
#else
static double qs5[6] = {
#endif
  8.12765501384335777857e+01, /* 0x405451B2, 0xFF5A11B2 */
  1.99179873460485964642e+03, /* 0x409F1F31, 0xE77BF839 */
  1.74684851924908907677e+04, /* 0x40D10F1F, 0x0D64CE29 */
  4.98514270910352279316e+04, /* 0x40E8576D, 0xAABAD197 */
  2.79480751638918118260e+04, /* 0x40DB4B04, 0xCF7C364B */
 -4.71918354795128470869e+03, /* 0xC0B26F2E, 0xFCFFA004 */
};

#ifdef __STDC__
static const double qr3[6] = {
#else
static double qr3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */
#endif
 -5.07831226461766561369e-09, /* 0xBE35CFA9, 0xD38FC84F */
 -1.02537829820837089745e-01, /* 0xBFBA3FEB, 0x51AEED54 */
 -4.61011581139473403113e+00, /* 0xC01270C2, 0x3302D9FF */
 -5.78472216562783643212e+01, /* 0xC04CEC71, 0xC25D16DA */
 -2.28244540737631695038e+02, /* 0xC06C87D3, 0x4718D55F */
 -2.19210128478909325622e+02, /* 0xC06B66B9, 0x5F5C1BF6 */
};
#ifdef __STDC__
static const double qs3[6] = {
#else
static double qs3[6] = {
#endif
  4.76651550323729509273e+01, /* 0x4047D523, 0xCCD367E4 */
  6.73865112676699709482e+02, /* 0x40850EEB, 0xC031EE3E */
  3.38015286679526343505e+03, /* 0x40AA684E, 0x448E7C9A */
  5.54772909720722782367e+03, /* 0x40B5ABBA, 0xA61D54A6 */
  1.90311919338810798763e+03, /* 0x409DBC7A, 0x0DD4DF4B */
 -1.35201191444307340817e+02, /* 0xC060E670, 0x290A311F */
};

#ifdef __STDC__
static const double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#else
static double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */
#endif
 -1.78381727510958865572e-07, /* 0xBE87F126, 0x44C626D2 */
 -1.02517042607985553460e-01, /* 0xBFBA3E8E, 0x9148B010 */
 -2.75220568278187460720e+00, /* 0xC0060484, 0x69BB4EDA */
 -1.96636162643703720221e+01, /* 0xC033A9E2, 0xC168907F */
 -4.23253133372830490089e+01, /* 0xC04529A3, 0xDE104AAA */
 -2.13719211703704061733e+01, /* 0xC0355F36, 0x39CF6E52 */
};
#ifdef __STDC__
static const double qs2[6] = {
#else
static double qs2[6] = {
#endif
  2.95333629060523854548e+01, /* 0x403D888A, 0x78AE64FF */
  2.52981549982190529136e+02, /* 0x406F9F68, 0xDB821CBA */
  7.57502834868645436472e+02, /* 0x4087AC05, 0xCE49A0F7 */
  7.39393205320467245656e+02, /* 0x40871B25, 0x48D4C029 */
  1.55949003336666123687e+02, /* 0x40637E5E, 0x3C3ED8D4 */
 -4.95949898822628210127e+00, /* 0xC013D686, 0xE71BE86B */
};

#ifdef __STDC__
	static double qone(double x)
#else
	static double qone(x)
	double x;
#endif
{
#ifdef __STDC__
	const double *p,*q;
#else
	double *p,*q;
#endif
	double  s,r,z;
	int ix;
	ix = 0x7fffffff&__HI(x);
	if(ix>=0x40200000)     {p = qr8; q= qs8;}
	else if(ix>=0x40122E8B){p = qr5; q= qs5;}
	else if(ix>=0x4006DB6D){p = qr3; q= qs3;}
	else if(ix>=0x40000000){p = qr2; q= qs2;}
	z = one/(x*x);
	r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
	s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
	return (.375 + r/s)/x;
}

**** End of e_j1.c. ****

**** Start of e_jn.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_jn.c 1.4 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * __ieee754_jn(n, x), __ieee754_yn(n, x)
 * floating point Bessel's function of the 1st and 2nd kind
 * of order n
 *          
 * Special cases:
 *	y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal;
 *	y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal.
 * Note 2. About jn(n,x), yn(n,x)
 *	For n=0, j0(x) is called,
 *	for n=1, j1(x) is called,
 *	for n<x, forward recursion us used starting
 *	from values of j0(x) and j1(x).
 *	for n>x, a continued fraction approximation to
 *	j(n,x)/j(n-1,x) is evaluated and then backward
 *	recursion is used starting from a supposed value
 *	for j(n,x). The resulting value of j(0,x) is
 *	compared with the actual value to correct the
 *	supposed value of j(n,x).
 *
 *	yn(n,x) is similar in all respects, except
 *	that forward recursion is used for all
 *	values of n>1.
 *	
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
invsqrtpi=  5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */
two   =  2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */
one   =  1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */

static double zero  =  0.00000000000000000000e+00;

#ifdef __STDC__
	double __ieee754_jn(int n, double x)
#else
	double __ieee754_jn(n,x)
	int n; double x;
#endif
{
	int i,hx,ix,lx, sgn;
	double a, b, temp, di;
	double z, w;

    /* J(-n,x) = (-1)^n * J(n, x), J(n, -x) = (-1)^n * J(n, x)
     * Thus, J(-n,x) = J(n,-x)
     */
	hx = __HI(x);
	ix = 0x7fffffff&hx;
	lx = __LO(x);
    /* if J(n,NaN) is NaN */
	if((ix|((unsigned)(lx|-lx))>>31)>0x7ff00000) return x+x;
	if(n<0){		
		n = -n;
		x = -x;
		hx ^= 0x80000000;
	}
	if(n==0) return(__ieee754_j0(x));
	if(n==1) return(__ieee754_j1(x));
	sgn = (n&1)&(hx>>31);	/* even n -- 0, odd n -- sign(x) */
	x = fd_fabs(x);
	if((ix|lx)==0||ix>=0x7ff00000) 	/* if x is 0 or inf */
	    b = zero;
	else if((double)n<=x) {   
		/* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */
	    if(ix>=0x52D00000) { /* x > 2**302 */
    /* (x >> n**2) 
     *	    Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi)
     *	    Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi)
     *	    Let s=sin(x), c=cos(x), 
     *		xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then
     *
     *		   n	sin(xn)*sqt2	cos(xn)*sqt2
     *		----------------------------------
     *		   0	 s-c		 c+s
     *		   1	-s-c 		-c+s
     *		   2	-s+c		-c-s
     *		   3	 s+c		 c-s
     */
		switch(n&3) {
		    case 0: temp =  fd_cos(x)+fd_sin(x); break;
		    case 1: temp = -fd_cos(x)+fd_sin(x); break;
		    case 2: temp = -fd_cos(x)-fd_sin(x); break;
		    case 3: temp =  fd_cos(x)-fd_sin(x); break;
		}
		b = invsqrtpi*temp/fd_sqrt(x);
	    } else {	
	        a = __ieee754_j0(x);
	        b = __ieee754_j1(x);
	        for(i=1;i<n;i++){
		    temp = b;
		    b = b*((double)(i+i)/x) - a; /* avoid underflow */
		    a = temp;
	        }
	    }
	} else {
	    if(ix<0x3e100000) {	/* x < 2**-29 */
    /* x is tiny, return the first Taylor expansion of J(n,x) 
     * J(n,x) = 1/n!*(x/2)^n  - ...
     */
		if(n>33)	/* underflow */
		    b = zero;
		else {
		    temp = x*0.5; b = temp;
		    for (a=one,i=2;i<=n;i++) {
			a *= (double)i;		/* a = n! */
			b *= temp;		/* b = (x/2)^n */
		    }
		    b = b/a;
		}
	    } else {
		/* use backward recurrence */
		/* 			x      x^2      x^2       
		 *  J(n,x)/J(n-1,x) =  ----   ------   ------   .....
		 *			2n  - 2(n+1) - 2(n+2)
		 *
		 * 			1      1        1       
		 *  (for large x)   =  ----  ------   ------   .....
		 *			2n   2(n+1)   2(n+2)
		 *			-- - ------ - ------ - 
		 *			 x     x         x
		 *
		 * Let w = 2n/x and h=2/x, then the above quotient
		 * is equal to the continued fraction:
		 *		    1
		 *	= -----------------------
		 *		       1
		 *	   w - -----------------
		 *			  1
		 * 	        w+h - ---------
		 *		       w+2h - ...
		 *
		 * To determine how many terms needed, let
		 * Q(0) = w, Q(1) = w(w+h) - 1,
		 * Q(k) = (w+k*h)*Q(k-1) - Q(k-2),
		 * When Q(k) > 1e4	good for single 
		 * When Q(k) > 1e9	good for double 
		 * When Q(k) > 1e17	good for quadruple 
		 */
	    /* determine k */
		double t,v;
		double q0,q1,h,tmp; int k,m;
		w  = (n+n)/(double)x; h = 2.0/(double)x;
		q0 = w;  z = w+h; q1 = w*z - 1.0; k=1;
		while(q1<1.0e9) {
			k += 1; z += h;
			tmp = z*q1 - q0;
			q0 = q1;
			q1 = tmp;
		}
		m = n+n;
		for(t=zero, i = 2*(n+k); i>=m; i -= 2) t = one/(i/x-t);
		a = t;
		b = one;
		/*  estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n)
		 *  Hence, if n*(log(2n/x)) > ...
		 *  single 8.8722839355e+01
		 *  double 7.09782712893383973096e+02
		 *  long double 1.1356523406294143949491931077970765006170e+04
		 *  then recurrent value may overflow and the result is 
		 *  likely underflow to zero
		 */
		tmp = n;
		v = two/x;
		tmp = tmp*__ieee754_log(fd_fabs(v*tmp));
		if(tmp<7.09782712893383973096e+02) {
	    	    for(i=n-1,di=(double)(i+i);i>0;i--){
		        temp = b;
			b *= di;
			b  = b/x - a;
		        a = temp;
			di -= two;
	     	    }
		} else {
	    	    for(i=n-1,di=(double)(i+i);i>0;i--){
		        temp = b;
			b *= di;
			b  = b/x - a;
		        a = temp;
			di -= two;
		    /* scale b to avoid spurious overflow */
			if(b>1e100) {
			    a /= b;
			    t /= b;
			    b  = one;
			}
	     	    }
		}
	    	b = (t*__ieee754_j0(x)/b);
	    }
	}
	if(sgn==1) return -b; else return b;
}

#ifdef __STDC__
	double __ieee754_yn(int n, double x) 
#else
	double __ieee754_yn(n,x) 
	int n; double x;
#endif
{
	int i,hx,ix,lx;
	int sign;
	double a, b, temp;

	hx = __HI(x);
	ix = 0x7fffffff&hx;
	lx = __LO(x);
    /* if Y(n,NaN) is NaN */
	if((ix|((unsigned)(lx|-lx))>>31)>0x7ff00000) return x+x;
	if((ix|lx)==0) return -one/zero;
	if(hx<0) return zero/zero;
	sign = 1;
	if(n<0){
		n = -n;
		sign = 1 - ((n&1)<<1);
	}
	if(n==0) return(__ieee754_y0(x));
	if(n==1) return(sign*__ieee754_y1(x));
	if(ix==0x7ff00000) return zero;
	if(ix>=0x52D00000) { /* x > 2**302 */
    /* (x >> n**2) 
     *	    Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi)
     *	    Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi)
     *	    Let s=sin(x), c=cos(x), 
     *		xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then
     *
     *		   n	sin(xn)*sqt2	cos(xn)*sqt2
     *		----------------------------------
     *		   0	 s-c		 c+s
     *		   1	-s-c 		-c+s
     *		   2	-s+c		-c-s
     *		   3	 s+c		 c-s
     */
		switch(n&3) {
		    case 0: temp =  fd_sin(x)-fd_cos(x); break;
		    case 1: temp = -fd_sin(x)-fd_cos(x); break;
		    case 2: temp = -fd_sin(x)+fd_cos(x); break;
		    case 3: temp =  fd_sin(x)+fd_cos(x); break;
		}
		b = invsqrtpi*temp/fd_sqrt(x);
	} else {
	    a = __ieee754_y0(x);
	    b = __ieee754_y1(x);
	/* quit if b is -inf */
	    for(i=1;i<n&&(__HI(b) != 0xfff00000);i++){ 
		temp = b;
		b = ((double)(i+i)/x)*b - a;
		a = temp;
	    }
	}
	if(sign>0) return b; else return -b;
}

**** End of e_jn.c. ****

**** Start of e_lgamma.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_lgamma.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_lgamma(x)
 * Return the logarithm of the Gamma function of x.
 *
 * Method: call __ieee754_lgamma_r
 */

#include "fdlibm.h"

extern int signgam;

#ifdef __STDC__
	double __ieee754_lgamma(double x)
#else
	double __ieee754_lgamma(x)
	double x;
#endif
{
	return __ieee754_lgamma_r(x,&signgam);
}

**** End of e_lgamma.c. ****

**** Start of e_lgamma_r.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_lgamma_r.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_lgamma_r(x, signgamp)
 * Reentrant version of the logarithm of the Gamma function 
 * with user provide pointer for the sign of Gamma(x). 
 *
 * Method:
 *   1. Argument Reduction for 0 < x <= 8
 * 	Since gamma(1+s)=s*gamma(s), for x in [0,8], we may 
 * 	reduce x to a number in [1.5,2.5] by
 * 		lgamma(1+s) = log(s) + lgamma(s)
 *	for example,
 *		lgamma(7.3) = log(6.3) + lgamma(6.3)
 *			    = log(6.3*5.3) + lgamma(5.3)
 *			    = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
 *   2. Polynomial approximation of lgamma around its
 *	minimun ymin=1.461632144968362245 to maintain monotonicity.
 *	On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
 *		Let z = x-ymin;
 *		lgamma(x) = -1.214862905358496078218 + z^2*poly(z)
 *	where
 *		poly(z) is a 14 degree polynomial.
 *   2. Rational approximation in the primary interval [2,3]
 *	We use the following approximation:
 *		s = x-2.0;
 *		lgamma(x) = 0.5*s + s*P(s)/Q(s)
 *	with accuracy
 *		|P/Q - (lgamma(x)-0.5s)| < 2**-61.71
 *	Our algorithms are based on the following observation
 *
 *                             zeta(2)-1    2    zeta(3)-1    3
 * lgamma(2+s) = s*(1-Euler) + --------- * s  -  --------- * s  + ...
 *                                 2                 3
 *
 *	where Euler = 0.5771... is the Euler constant, which is very
 *	close to 0.5.
 *
 *   3. For x>=8, we have
 *	lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
 *	(better formula:
 *	   lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
 *	Let z = 1/x, then we approximation
 *		f(z) = lgamma(x) - (x-0.5)(log(x)-1)
 *	by
 *	  			    3       5             11
 *		w = w0 + w1*z + w2*z  + w3*z  + ... + w6*z
 *	where 
 *		|w - f(z)| < 2**-58.74
 *		
 *   4. For negative x, since (G is gamma function)
 *		-x*G(-x)*G(x) = pi/sin(pi*x),
 * 	we have
 * 		G(x) = pi/(sin(pi*x)*(-x)*G(-x))
 *	since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
 *	Hence, for x<0, signgam = sign(sin(pi*x)) and 
 *		lgamma(x) = log(|Gamma(x)|)
 *			  = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
 *	Note: one should avoid compute pi*(-x) directly in the 
 *	      computation of sin(pi*(-x)).
 *		
 *   5. Special Cases
 *		lgamma(2+s) ~ s*(1-Euler) for tiny s
 *		lgamma(1)=lgamma(2)=0
 *		lgamma(x) ~ -log(x) for tiny x
 *		lgamma(0) = lgamma(inf) = inf
 *	 	lgamma(-integer) = +-inf
 *	
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
two52=  4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */
half=  5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */
one =  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
pi  =  3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */
a0  =  7.72156649015328655494e-02, /* 0x3FB3C467, 0xE37DB0C8 */
a1  =  3.22467033424113591611e-01, /* 0x3FD4A34C, 0xC4A60FAD */
a2  =  6.73523010531292681824e-02, /* 0x3FB13E00, 0x1A5562A7 */
a3  =  2.05808084325167332806e-02, /* 0x3F951322, 0xAC92547B */
a4  =  7.38555086081402883957e-03, /* 0x3F7E404F, 0xB68FEFE8 */
a5  =  2.89051383673415629091e-03, /* 0x3F67ADD8, 0xCCB7926B */
a6  =  1.19270763183362067845e-03, /* 0x3F538A94, 0x116F3F5D */
a7  =  5.10069792153511336608e-04, /* 0x3F40B6C6, 0x89B99C00 */
a8  =  2.20862790713908385557e-04, /* 0x3F2CF2EC, 0xED10E54D */
a9  =  1.08011567247583939954e-04, /* 0x3F1C5088, 0x987DFB07 */
a10 =  2.52144565451257326939e-05, /* 0x3EFA7074, 0x428CFA52 */
a11 =  4.48640949618915160150e-05, /* 0x3F07858E, 0x90A45837 */
tc  =  1.46163214496836224576e+00, /* 0x3FF762D8, 0x6356BE3F */
tf  = -1.21486290535849611461e-01, /* 0xBFBF19B9, 0xBCC38A42 */
/* tt = -(tail of tf) */
tt  = -3.63867699703950536541e-18, /* 0xBC50C7CA, 0xA48A971F */
t0  =  4.83836122723810047042e-01, /* 0x3FDEF72B, 0xC8EE38A2 */
t1  = -1.47587722994593911752e-01, /* 0xBFC2E427, 0x8DC6C509 */
t2  =  6.46249402391333854778e-02, /* 0x3FB08B42, 0x94D5419B */
t3  = -3.27885410759859649565e-02, /* 0xBFA0C9A8, 0xDF35B713 */
t4  =  1.79706750811820387126e-02, /* 0x3F9266E7, 0x970AF9EC */
t5  = -1.03142241298341437450e-02, /* 0xBF851F9F, 0xBA91EC6A */
t6  =  6.10053870246291332635e-03, /* 0x3F78FCE0, 0xE370E344 */
t7  = -3.68452016781138256760e-03, /* 0xBF6E2EFF, 0xB3E914D7 */
t8  =  2.25964780900612472250e-03, /* 0x3F6282D3, 0x2E15C915 */
t9  = -1.40346469989232843813e-03, /* 0xBF56FE8E, 0xBF2D1AF1 */
t10 =  8.81081882437654011382e-04, /* 0x3F4CDF0C, 0xEF61A8E9 */
t11 = -5.38595305356740546715e-04, /* 0xBF41A610, 0x9C73E0EC */
t12 =  3.15632070903625950361e-04, /* 0x3F34AF6D, 0x6C0EBBF7 */
t13 = -3.12754168375120860518e-04, /* 0xBF347F24, 0xECC38C38 */
t14 =  3.35529192635519073543e-04, /* 0x3F35FD3E, 0xE8C2D3F4 */
u0  = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */
u1  =  6.32827064025093366517e-01, /* 0x3FE4401E, 0x8B005DFF */
u2  =  1.45492250137234768737e+00, /* 0x3FF7475C, 0xD119BD6F */
u3  =  9.77717527963372745603e-01, /* 0x3FEF4976, 0x44EA8450 */
u4  =  2.28963728064692451092e-01, /* 0x3FCD4EAE, 0xF6010924 */
u5  =  1.33810918536787660377e-02, /* 0x3F8B678B, 0xBF2BAB09 */
v1  =  2.45597793713041134822e+00, /* 0x4003A5D7, 0xC2BD619C */
v2  =  2.12848976379893395361e+00, /* 0x40010725, 0xA42B18F5 */
v3  =  7.69285150456672783825e-01, /* 0x3FE89DFB, 0xE45050AF */
v4  =  1.04222645593369134254e-01, /* 0x3FBAAE55, 0xD6537C88 */
v5  =  3.21709242282423911810e-03, /* 0x3F6A5ABB, 0x57D0CF61 */
s0  = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */
s1  =  2.14982415960608852501e-01, /* 0x3FCB848B, 0x36E20878 */
s2  =  3.25778796408930981787e-01, /* 0x3FD4D98F, 0x4F139F59 */
s3  =  1.46350472652464452805e-01, /* 0x3FC2BB9C, 0xBEE5F2F7 */
s4  =  2.66422703033638609560e-02, /* 0x3F9B481C, 0x7E939961 */
s5  =  1.84028451407337715652e-03, /* 0x3F5E26B6, 0x7368F239 */
s6  =  3.19475326584100867617e-05, /* 0x3F00BFEC, 0xDD17E945 */
r1  =  1.39200533467621045958e+00, /* 0x3FF645A7, 0x62C4AB74 */
r2  =  7.21935547567138069525e-01, /* 0x3FE71A18, 0x93D3DCDC */
r3  =  1.71933865632803078993e-01, /* 0x3FC601ED, 0xCCFBDF27 */
r4  =  1.86459191715652901344e-02, /* 0x3F9317EA, 0x742ED475 */
r5  =  7.77942496381893596434e-04, /* 0x3F497DDA, 0xCA41A95B */
r6  =  7.32668430744625636189e-06, /* 0x3EDEBAF7, 0xA5B38140 */
w0  =  4.18938533204672725052e-01, /* 0x3FDACFE3, 0x90C97D69 */
w1  =  8.33333333333329678849e-02, /* 0x3FB55555, 0x5555553B */
w2  = -2.77777777728775536470e-03, /* 0xBF66C16C, 0x16B02E5C */
w3  =  7.93650558643019558500e-04, /* 0x3F4A019F, 0x98CF38B6 */
w4  = -5.95187557450339963135e-04, /* 0xBF4380CB, 0x8C0FE741 */
w5  =  8.36339918996282139126e-04, /* 0x3F4B67BA, 0x4CDAD5D1 */
w6  = -1.63092934096575273989e-03; /* 0xBF5AB89D, 0x0B9E43E4 */

static double zero=  0.00000000000000000000e+00;

#ifdef __STDC__
	static double sin_pi(double x)
#else
	static double sin_pi(x)
	double x;
#endif
{
	double y,z;
	int n,ix;

	ix = 0x7fffffff&__HI(x);

	if(ix<0x3fd00000) return __kernel_sin(pi*x,zero,0);
	y = -x;		/* x is assume negative */

    /*
     * argument reduction, make sure inexact flag not raised if input
     * is an integer
     */
	z = fd_floor(y);
	if(z!=y) {				/* inexact anyway */
	    y  *= 0.5;
	    y   = 2.0*(y - fd_floor(y));		/* y = |x| mod 2.0 */
	    n   = (int) (y*4.0);
	} else {
            if(ix>=0x43400000) {
                y = zero; n = 0;                 /* y must be even */
            } else {
                if(ix<0x43300000) z = y+two52;	/* exact */
                n   = __LO(z)&1;        /* lower word of z */
                y  = n;
                n<<= 2;
            }
        }
	switch (n) {
	    case 0:   y =  __kernel_sin(pi*y,zero,0); break;
	    case 1:   
	    case 2:   y =  __kernel_cos(pi*(0.5-y),zero); break;
	    case 3:  
	    case 4:   y =  __kernel_sin(pi*(one-y),zero,0); break;
	    case 5:
	    case 6:   y = -__kernel_cos(pi*(y-1.5),zero); break;
	    default:  y =  __kernel_sin(pi*(y-2.0),zero,0); break;
	    }
	return -y;
}


#ifdef __STDC__
	double __ieee754_lgamma_r(double x, int *signgamp)
#else
	double __ieee754_lgamma_r(x,signgamp)
	double x; int *signgamp;
#endif
{
	double t,y,z,nadj,p,p1,p2,p3,q,r,w;
	int i,hx,lx,ix;

	hx = __HI(x);
	lx = __LO(x);

    /* purge off +-inf, NaN, +-0, and negative arguments */
	*signgamp = 1;
	ix = hx&0x7fffffff;
	if(ix>=0x7ff00000) return x*x;
	if((ix|lx)==0) return one/zero;
	if(ix<0x3b900000) {	/* |x|<2**-70, return -log(|x|) */
	    if(hx<0) {
	        *signgamp = -1;
	        return -__ieee754_log(-x);
	    } else return -__ieee754_log(x);
	}
	if(hx<0) {
	    if(ix>=0x43300000) 	/* |x|>=2**52, must be -integer */
		return one/zero;
	    t = sin_pi(x);
	    if(t==zero) return one/zero; /* -integer */
	    nadj = __ieee754_log(pi/fd_fabs(t*x));
	    if(t<zero) *signgamp = -1;
	    x = -x;
	}

    /* purge off 1 and 2 */
	if((((ix-0x3ff00000)|lx)==0)||(((ix-0x40000000)|lx)==0)) r = 0;
    /* for x < 2.0 */
	else if(ix<0x40000000) {
	    if(ix<=0x3feccccc) { 	/* lgamma(x) = lgamma(x+1)-log(x) */
		r = -__ieee754_log(x);
		if(ix>=0x3FE76944) {y = one-x; i= 0;}
		else if(ix>=0x3FCDA661) {y= x-(tc-one); i=1;}
	  	else {y = x; i=2;}
	    } else {
	  	r = zero;
	        if(ix>=0x3FFBB4C3) {y=2.0-x;i=0;} /* [1.7316,2] */
	        else if(ix>=0x3FF3B4C4) {y=x-tc;i=1;} /* [1.23,1.73] */
		else {y=x-one;i=2;}
	    }
	    switch(i) {
	      case 0:
		z = y*y;
		p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10))));
		p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11)))));
		p  = y*p1+p2;
		r  += (p-0.5*y); break;
	      case 1:
		z = y*y;
		w = z*y;
		p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12)));	/* parallel comp */
		p2 = t1+w*(t4+w*(t7+w*(t10+w*t13)));
		p3 = t2+w*(t5+w*(t8+w*(t11+w*t14)));
		p  = z*p1-(tt-w*(p2+y*p3));
		r += (tf + p); break;
	      case 2:	
		p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5)))));
		p2 = one+y*(v1+y*(v2+y*(v3+y*(v4+y*v5))));
		r += (-0.5*y + p1/p2);
	    }
	}
	else if(ix<0x40200000) { 			/* x < 8.0 */
	    i = (int)x;
	    t = zero;
	    y = x-(double)i;
	    p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6))))));
	    q = one+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6)))));
	    r = half*y+p/q;
	    z = one;	/* lgamma(1+s) = log(s) + lgamma(s) */
	    switch(i) {
	    case 7: z *= (y+6.0);	/* FALLTHRU */
	    case 6: z *= (y+5.0);	/* FALLTHRU */
	    case 5: z *= (y+4.0);	/* FALLTHRU */
	    case 4: z *= (y+3.0);	/* FALLTHRU */
	    case 3: z *= (y+2.0);	/* FALLTHRU */
		    r += __ieee754_log(z); break;
	    }
    /* 8.0 <= x < 2**58 */
	} else if (ix < 0x43900000) {
	    t = __ieee754_log(x);
	    z = one/x;
	    y = z*z;
	    w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6)))));
	    r = (x-half)*(t-one)+w;
	} else 
    /* 2**58 <= x <= inf */
	    r =  x*(__ieee754_log(x)-one);
	if(hx<0) r = nadj - r;
	return r;
}

**** End of e_lgamma_r.c. ****

**** Start of e_log.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_log.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_log(x)
 * Return the logrithm of x
 *
 * Method :                  
 *   1. Argument Reduction: find k and f such that 
 *			x = 2^k * (1+f), 
 *	   where  sqrt(2)/2 < 1+f < sqrt(2) .
 *
 *   2. Approximation of log(1+f).
 *	Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
 *		 = 2s + 2/3 s**3 + 2/5 s**5 + .....,
 *	     	 = 2s + s*R
 *      We use a special Reme algorithm on [0,0.1716] to generate 
 * 	a polynomial of degree 14 to approximate R The maximum error 
 *	of this polynomial approximation is bounded by 2**-58.45. In
 *	other words,
 *		        2      4      6      8      10      12      14
 *	    R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s  +Lg6*s  +Lg7*s
 *  	(the values of Lg1 to Lg7 are listed in the program)
 *	and
 *	    |      2          14          |     -58.45
 *	    | Lg1*s +...+Lg7*s    -  R(z) | <= 2 
 *	    |                             |
 *	Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
 *	In order to guarantee error in log below 1ulp, we compute log
 *	by
 *		log(1+f) = f - s*(f - R)	(if f is not too large)
 *		log(1+f) = f - (hfsq - s*(hfsq+R)).	(better accuracy)
 *	
 *	3. Finally,  log(x) = k*ln2 + log(1+f).  
 *			    = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
 *	   Here ln2 is split into two floating point number: 
 *			ln2_hi + ln2_lo,
 *	   where n*ln2_hi is always exact for |n| < 2000.
 *
 * Special cases:
 *	log(x) is NaN with signal if x < 0 (including -INF) ; 
 *	log(+INF) is +INF; log(0) is -INF with signal;
 *	log(NaN) is that NaN with no signal.
 *
 * Accuracy:
 *	according to an error analysis, the error is always less than
 *	1 ulp (unit in the last place).
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
ln2_hi  =  6.93147180369123816490e-01,	/* 3fe62e42 fee00000 */
ln2_lo  =  1.90821492927058770002e-10,	/* 3dea39ef 35793c76 */
two54   =  1.80143985094819840000e+16,  /* 43500000 00000000 */
Lg1 = 6.666666666666735130e-01,  /* 3FE55555 55555593 */
Lg2 = 3.999999999940941908e-01,  /* 3FD99999 9997FA04 */
Lg3 = 2.857142874366239149e-01,  /* 3FD24924 94229359 */
Lg4 = 2.222219843214978396e-01,  /* 3FCC71C5 1D8E78AF */
Lg5 = 1.818357216161805012e-01,  /* 3FC74664 96CB03DE */
Lg6 = 1.531383769920937332e-01,  /* 3FC39A09 D078C69F */
Lg7 = 1.479819860511658591e-01;  /* 3FC2F112 DF3E5244 */

static double zero   =  0.0;

#ifdef __STDC__
	double __ieee754_log(double x)
#else
	double __ieee754_log(x)
	double x;
#endif
{
	double hfsq,f,s,z,R,w,t1,t2,dk;
	int k,hx,i,j;
	unsigned lx;

	hx = __HI(x);		/* high word of x */
	lx = __LO(x);		/* low  word of x */

	k=0;
	if (hx < 0x00100000) {			/* x < 2**-1022  */
	    if (((hx&0x7fffffff)|lx)==0) 
		return -two54/zero;		/* log(+-0)=-inf */
	    if (hx<0) return (x-x)/zero;	/* log(-#) = NaN */
	    k -= 54; x *= two54; /* subnormal number, scale up x */
	    hx = __HI(x);		/* high word of x */
	} 
	if (hx >= 0x7ff00000) return x+x;
	k += (hx>>20)-1023;
	hx &= 0x000fffff;
	i = (hx+0x95f64)&0x100000;
	__HI(x) = hx|(i^0x3ff00000);	/* normalize x or x/2 */
	k += (i>>20);
	f = x-1.0;
	if((0x000fffff&(2+hx))<3) {	/* |f| < 2**-20 */
	    if(f==zero) if(k==0) return zero;  else {dk=(double)k;
				 return dk*ln2_hi+dk*ln2_lo;}
	    R = f*f*(0.5-0.33333333333333333*f);
	    if(k==0) return f-R; else {dk=(double)k;
	    	     return dk*ln2_hi-((R-dk*ln2_lo)-f);}
	}
 	s = f/(2.0+f); 
	dk = (double)k;
	z = s*s;
	i = hx-0x6147a;
	w = z*z;
	j = 0x6b851-hx;
	t1= w*(Lg2+w*(Lg4+w*Lg6)); 
	t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); 
	i |= j;
	R = t2+t1;
	if(i>0) {
	    hfsq=0.5*f*f;
	    if(k==0) return f-(hfsq-s*(hfsq+R)); else
		     return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f);
	} else {
	    if(k==0) return f-s*(f-R); else
		     return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f);
	}
}

**** End of e_log.c. ****

**** Start of e_log10.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_log10.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_log10(x)
 * Return the base 10 logarithm of x
 * 
 * Method :
 *	Let log10_2hi = leading 40 bits of log10(2) and
 *	    log10_2lo = log10(2) - log10_2hi,
 *	    ivln10   = 1/log(10) rounded.
 *	Then
 *		n = ilogb(x), 
 *		if(n<0)  n = n+1;
 *		x = scalbn(x,-n);
 *		log10(x) := n*log10_2hi + (n*log10_2lo + ivln10*log(x))
 *
 * Note 1:
 *	To guarantee log10(10**n)=n, where 10**n is normal, the rounding 
 *	mode must set to Round-to-Nearest.
 * Note 2:
 *	[1/log(10)] rounded to 53 bits has error  .198   ulps;
 *	log10 is monotonic at all binary break points.
 *
 * Special cases:
 *	log10(x) is NaN with signal if x < 0; 
 *	log10(+INF) is +INF with no signal; log10(0) is -INF with signal;
 *	log10(NaN) is that NaN with no signal;
 *	log10(10**N) = N  for N=0,1,...,22.
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following constants.
 * The decimal values may be used, provided that the compiler will convert
 * from decimal to binary accurately enough to produce the hexadecimal values
 * shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
two54      =  1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */
ivln10     =  4.34294481903251816668e-01, /* 0x3FDBCB7B, 0x1526E50E */
log10_2hi  =  3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */
log10_2lo  =  3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */

static double zero   =  0.0;

#ifdef __STDC__
	double __ieee754_log10(double x)
#else
	double __ieee754_log10(x)
	double x;
#endif
{
	double y,z;
	int i,k,hx;
	unsigned lx;

	hx = __HI(x);	/* high word of x */
	lx = __LO(x);	/* low word of x */

        k=0;
        if (hx < 0x00100000) {                  /* x < 2**-1022  */
            if (((hx&0x7fffffff)|lx)==0)
                return -two54/zero;             /* log(+-0)=-inf */
            if (hx<0) return (x-x)/zero;        /* log(-#) = NaN */
            k -= 54; x *= two54; /* subnormal number, scale up x */
            hx = __HI(x);                /* high word of x */
        }
	if (hx >= 0x7ff00000) return x+x;
	k += (hx>>20)-1023;
	i  = ((unsigned)k&0x80000000)>>31;
        hx = (hx&0x000fffff)|((0x3ff-i)<<20);
        y  = (double)(k+i);
        __HI(x) = hx;
	z  = y*log10_2lo + ivln10*__ieee754_log(x);
	return  z+y*log10_2hi;
}

**** End of e_log10.c. ****

**** Start of e_pow.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_pow.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_pow(x,y) return x**y
 *
 *		      n
 * Method:  Let x =  2   * (1+f)
 *	1. Compute and return log2(x) in two pieces:
 *		log2(x) = w1 + w2,
 *	   where w1 has 53-24 = 29 bit trailing zeros.
 *	2. Perform y*log2(x) = n+y' by simulating muti-precision 
 *	   arithmetic, where |y'|<=0.5.
 *	3. Return x**y = 2**n*exp(y'*log2)
 *
 * Special cases:
 *	1.  (anything) ** 0  is 1
 *	2.  (anything) ** 1  is itself
 *	3.  (anything) ** NAN is NAN
 *	4.  NAN ** (anything except 0) is NAN
 *	5.  +-(|x| > 1) **  +INF is +INF
 *	6.  +-(|x| > 1) **  -INF is +0
 *	7.  +-(|x| < 1) **  +INF is +0
 *	8.  +-(|x| < 1) **  -INF is +INF
 *	9.  +-1         ** +-INF is NAN
 *	10. +0 ** (+anything except 0, NAN)               is +0
 *	11. -0 ** (+anything except 0, NAN, odd integer)  is +0
 *	12. +0 ** (-anything except 0, NAN)               is +INF
 *	13. -0 ** (-anything except 0, NAN, odd integer)  is +INF
 *	14. -0 ** (odd integer) = -( +0 ** (odd integer) )
 *	15. +INF ** (+anything except 0,NAN) is +INF
 *	16. +INF ** (-anything except 0,NAN) is +0
 *	17. -INF ** (anything)  = -0 ** (-anything)
 *	18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer)
 *	19. (-anything except 0 and inf) ** (non-integer) is NAN
 *
 * Accuracy:
 *	pow(x,y) returns x**y nearly rounded. In particular
 *			pow(integer,integer)
 *	always returns the correct integer provided it is 
 *	representable.
 *
 * Constants :
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
bp[] = {1.0, 1.5,},
dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */
dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */
zero    =  0.0,
one	=  1.0,
two	=  2.0,
two53	=  9007199254740992.0,	/* 0x43400000, 0x00000000 */
huge	=  1.0e300,
tiny    =  1.0e-300,
	/* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */
L1  =  5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */
L2  =  4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */
L3  =  3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */
L4  =  2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */
L5  =  2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */
L6  =  2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */
P1   =  1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */
P2   = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */
P3   =  6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */
P4   = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */
P5   =  4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */
lg2  =  6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */
lg2_h  =  6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */
lg2_l  = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */
ovt =  8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */
cp    =  9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */
cp_h  =  9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */
cp_l  = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/
ivln2    =  1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */
ivln2_h  =  1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/
ivln2_l  =  1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/

#ifdef __STDC__
	double __ieee754_pow(double x, double y)
#else
	double __ieee754_pow(x,y)
	double x, y;
#endif
{
	double z,ax,z_h,z_l,p_h,p_l;
	double y1,t1,t2,r,s,t,u,v,w;
	int i,j,k,yisint,n;
	int hx,hy,ix,iy;
	unsigned lx,ly;

	hx = __HI(x); lx = __LO(x);
	hy = __HI(y); ly = __LO(y);
	ix = hx&0x7fffffff;  iy = hy&0x7fffffff;

    /* y==zero: x**0 = 1 */
	if((iy|ly)==0) return one; 	

    /* +-NaN return x+y */
	if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) ||
	   iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) 
		return x+y;	

    /* determine if y is an odd int when x < 0
     * yisint = 0	... y is not an integer
     * yisint = 1	... y is an odd int
     * yisint = 2	... y is an even int
     */
	yisint  = 0;
	if(hx<0) {	
	    if(iy>=0x43400000) yisint = 2; /* even integer y */
	    else if(iy>=0x3ff00000) {
		k = (iy>>20)-0x3ff;	   /* exponent */
		if(k>20) {
		    j = ly>>(52-k);
		    if((j<<(52-k))==(int)ly) yisint = 2-(j&1);
		} else if(ly==0) {
		    j = iy>>(20-k);
		    if((j<<(20-k))==iy) yisint = 2-(j&1);
		}
	    }		
	} 

    /* special value of y */
	if(ly==0) { 	
	    if (iy==0x7ff00000) {	/* y is +-inf */
	        if(((ix-0x3ff00000)|lx)==0)
		    return  y - y;	/* inf**+-1 is NaN */
	        else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */
		    return (hy>=0)? y: zero;
	        else			/* (|x|<1)**-,+inf = inf,0 */
		    return (hy<0)?-y: zero;
	    } 
	    if(iy==0x3ff00000) {	/* y is  +-1 */
		if(hy<0) return one/x; else return x;
	    }
	    if(hy==0x40000000) return x*x; /* y is  2 */
	    if(hy==0x3fe00000) {	/* y is  0.5 */
		if(hx>=0)	/* x >= +0 */
		return fd_sqrt(x);	
	    }
	}

	ax   = fd_fabs(x);
    /* special value of x */
	if(lx==0) {
	    if(ix==0x7ff00000||ix==0||ix==0x3ff00000){
		z = ax;			/*x is +-0,+-inf,+-1*/
		if(hy<0) z = one/z;	/* z = (1/|x|) */
		if(hx<0) {
		    if(((ix-0x3ff00000)|yisint)==0) {
			z = (z-z)/(z-z); /* (-1)**non-int is NaN */
		    } else if(yisint==1) {
#ifdef HPUX
			__HI(z) ^= 1<<31; /* some HPUXes cannot negate 0.. */
#else
			z = -z;		/* (x<0)**odd = -(|x|**odd) */
#endif
			}
		}
		return z;
	    }
	}
    
    /* (x<0)**(non-int) is NaN */
	if((((hx>>31)+1)|yisint)==0) return (x-x)/(x-x);

    /* |y| is huge */
	if(iy>0x41e00000) { /* if |y| > 2**31 */
	    if(iy>0x43f00000){	/* if |y| > 2**64, must o/uflow */
		if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny;
		if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny;
	    }
	/* over/underflow if x is not close to one */
	    if(ix<0x3fefffff) return (hy<0)? huge*huge:tiny*tiny;
	    if(ix>0x3ff00000) return (hy>0)? huge*huge:tiny*tiny;
	/* now |1-x| is tiny <= 2**-20, suffice to compute 
	   log(x) by x-x^2/2+x^3/3-x^4/4 */
	    t = x-1;		/* t has 20 trailing zeros */
	    w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25));
	    u = ivln2_h*t;	/* ivln2_h has 21 sig. bits */
	    v = t*ivln2_l-w*ivln2;
	    t1 = u+v;
	    __LO(t1) = 0;
	    t2 = v-(t1-u);
	} else {
	    double s2,s_h,s_l,t_h,t_l;
	    n = 0;
	/* take care subnormal number */
	    if(ix<0x00100000)
		{ax *= two53; n -= 53; ix = __HI(ax); }
	    n  += ((ix)>>20)-0x3ff;
	    j  = ix&0x000fffff;
	/* determine interval */
	    ix = j|0x3ff00000;		/* normalize ix */
	    if(j<=0x3988E) k=0;		/* |x|<sqrt(3/2) */
	    else if(j<0xBB67A) k=1;	/* |x|<sqrt(3)   */
	    else {k=0;n+=1;ix -= 0x00100000;}
	    __HI(ax) = ix;

	/* compute s = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */
	    u = ax-bp[k];		/* bp[0]=1.0, bp[1]=1.5 */
	    v = one/(ax+bp[k]);
	    s = u*v;
	    s_h = s;
	    __LO(s_h) = 0;
	/* t_h=ax+bp[k] High */
	    t_h = zero;
	    __HI(t_h)=((ix>>1)|0x20000000)+0x00080000+(k<<18); 
	    t_l = ax - (t_h-bp[k]);
	    s_l = v*((u-s_h*t_h)-s_h*t_l);
	/* compute log(ax) */
	    s2 = s*s;
	    r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6)))));
	    r += s_l*(s_h+s);
	    s2  = s_h*s_h;
	    t_h = 3.0+s2+r;
	    __LO(t_h) = 0;
	    t_l = r-((t_h-3.0)-s2);
	/* u+v = s*(1+...) */
	    u = s_h*t_h;
	    v = s_l*t_h+t_l*s;
	/* 2/(3log2)*(s+...) */
	    p_h = u+v;
	    __LO(p_h) = 0;
	    p_l = v-(p_h-u);
	    z_h = cp_h*p_h;		/* cp_h+cp_l = 2/(3*log2) */
	    z_l = cp_l*p_h+p_l*cp+dp_l[k];
	/* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */
	    t = (double)n;
	    t1 = (((z_h+z_l)+dp_h[k])+t);
	    __LO(t1) = 0;
	    t2 = z_l-(((t1-t)-dp_h[k])-z_h);
	}

	s = one; /* s (sign of result -ve**odd) = -1 else = 1 */
	if((((hx>>31)+1)|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */

    /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */
	y1  = y;
	__LO(y1) = 0;
	p_l = (y-y1)*t1+y*t2;
	p_h = y1*t1;
	z = p_l+p_h;
	j = __HI(z);
	i = __LO(z);
	if (j>=0x40900000) {				/* z >= 1024 */
	    if(((j-0x40900000)|i)!=0)			/* if z > 1024 */
		return s*huge*huge;			/* overflow */
	    else {
		if(p_l+ovt>z-p_h) return s*huge*huge;	/* overflow */
	    }
	} else if((j&0x7fffffff)>=0x4090cc00 ) {	/* z <= -1075 */
	    if(((j-0xc090cc00)|i)!=0) 		/* z < -1075 */
		return s*tiny*tiny;		/* underflow */
	    else {
		if(p_l<=z-p_h) return s*tiny*tiny;	/* underflow */
	    }
	}
    /*
     * compute 2**(p_h+p_l)
     */
	i = j&0x7fffffff;
	k = (i>>20)-0x3ff;
	n = 0;
	if(i>0x3fe00000) {		/* if |z| > 0.5, set n = [z+0.5] */
	    n = j+(0x00100000>>(k+1));
	    k = ((n&0x7fffffff)>>20)-0x3ff;	/* new k for n */
	    t = zero;
	    __HI(t) = (n&~(0x000fffff>>k));
	    n = ((n&0x000fffff)|0x00100000)>>(20-k);
	    if(j<0) n = -n;
	    p_h -= t;
	} 
	t = p_l+p_h;
	__LO(t) = 0;
	u = t*lg2_h;
	v = (p_l-(t-p_h))*lg2+t*lg2_l;
	z = u+v;
	w = v-(z-u);
	t  = z*z;
	t1  = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))));
	r  = (z*t1)/(t1-two)-(w+z*w);
	z  = one-(r-z);
	j  = __HI(z);
	j += (n<<20);
	if((j>>20)<=0) z = fd_scalbn(z,n);	/* subnormal output */
	else __HI(z) += (n<<20);
	return s*z;
}

**** End of e_pow.c. ****

**** Start of e_remainder.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_remainder.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_remainder(x,p)
 * Return :                  
 * 	returns  x REM p  =  x - [x/p]*p as if in infinite 
 * 	precise arithmetic, where [x/p] is the (infinite bit) 
 *	integer nearest x/p (in half way case choose the even one).
 * Method : 
 *	Based on fmod() return x-[x/p]chopped*p exactlp.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double zero = 0.0;
#else
static double zero = 0.0;
#endif


#ifdef __STDC__
	double __ieee754_remainder(double x, double p)
#else
	double __ieee754_remainder(x,p)
	double x,p;
#endif
{
	int hx,hp;
	unsigned sx,lx,lp;
	double p_half;

	hx = __HI(x);		/* high word of x */
	lx = __LO(x);		/* low  word of x */
	hp = __HI(p);		/* high word of p */
	lp = __LO(p);		/* low  word of p */
	sx = hx&0x80000000;
	hp &= 0x7fffffff;
	hx &= 0x7fffffff;

    /* purge off exception values */
	if((hp|lp)==0) return (x*p)/(x*p); 	/* p = 0 */
	if((hx>=0x7ff00000)||			/* x not finite */
	  ((hp>=0x7ff00000)&&			/* p is NaN */
	  (((hp-0x7ff00000)|lp)!=0)))
	    return (x*p)/(x*p);


	if (hp<=0x7fdfffff) x = __ieee754_fmod(x,p+p);	/* now x < 2p */
	if (((hx-hp)|(lx-lp))==0) return zero*x;
	x  = fd_fabs(x);
	p  = fd_fabs(p);
	if (hp<0x00200000) {
	    if(x+x>p) {
		x-=p;
		if(x+x>=p) x -= p;
	    }
	} else {
	    p_half = 0.5*p;
	    if(x>p_half) {
		x-=p;
		if(x>=p_half) x -= p;
	    }
	}
	__HI(x) ^= sx;
	return x;
}

**** End of e_remainder.c. ****

**** Start of e_rem_pio2.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_rem_pio2.c 1.4 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* __ieee754_rem_pio2(x,y)
 * 
 * return the remainder of x rem pi/2 in y[0]+y[1] 
 * use __kernel_rem_pio2()
 */

#include "fdlibm.h"

/*
 * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi 
 */
#ifdef __STDC__
static const int two_over_pi[] = {
#else
static int two_over_pi[] = {
#endif
0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 
0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, 
0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, 
0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, 
0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, 
0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, 
0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, 
0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, 
0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, 
0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, 
0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, 
};

#ifdef __STDC__
static const int npio2_hw[] = {
#else
static int npio2_hw[] = {
#endif
0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C,
0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C,
0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A,
0x403DD85A, 0x403F6A7A, 0x40407E4C, 0x4041475C, 0x4042106C, 0x4042D97C,
0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, 0x4046C6CB, 0x40478FDB,
0x404858EB, 0x404921FB,
};

/*
 * invpio2:  53 bits of 2/pi
 * pio2_1:   first  33 bit of pi/2
 * pio2_1t:  pi/2 - pio2_1
 * pio2_2:   second 33 bit of pi/2
 * pio2_2t:  pi/2 - (pio2_1+pio2_2)
 * pio2_3:   third  33 bit of pi/2
 * pio2_3t:  pi/2 - (pio2_1+pio2_2+pio2_3)
 */

#ifdef __STDC__
static const double 
#else
static double 
#endif
zero =  0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */
half =  5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */
two24 =  1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */
invpio2 =  6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
pio2_1  =  1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */
pio2_1t =  6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */
pio2_2  =  6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */
pio2_2t =  2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */
pio2_3  =  2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */
pio2_3t =  8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */

#ifdef __STDC__
	int __ieee754_rem_pio2(double x, double *y)
#else
	int __ieee754_rem_pio2(x,y)
	double x,y[];
#endif
{
	double z,w,t,r,fn;
	double tx[3];
	int e0,i,j,nx,n,ix,hx;

	hx = __HI(x);		/* high word of x */
	ix = hx&0x7fffffff;
	if(ix<=0x3fe921fb)   /* |x| ~<= pi/4 , no need for reduction */
	    {y[0] = x; y[1] = 0; return 0;}
	if(ix<0x4002d97c) {  /* |x| < 3pi/4, special case with n=+-1 */
	    if(hx>0) { 
		z = x - pio2_1;
		if(ix!=0x3ff921fb) { 	/* 33+53 bit pi is good enough */
		    y[0] = z - pio2_1t;
		    y[1] = (z-y[0])-pio2_1t;
		} else {		/* near pi/2, use 33+33+53 bit pi */
		    z -= pio2_2;
		    y[0] = z - pio2_2t;
		    y[1] = (z-y[0])-pio2_2t;
		}
		return 1;
	    } else {	/* negative x */
		z = x + pio2_1;
		if(ix!=0x3ff921fb) { 	/* 33+53 bit pi is good enough */
		    y[0] = z + pio2_1t;
		    y[1] = (z-y[0])+pio2_1t;
		} else {		/* near pi/2, use 33+33+53 bit pi */
		    z += pio2_2;
		    y[0] = z + pio2_2t;
		    y[1] = (z-y[0])+pio2_2t;
		}
		return -1;
	    }
	}
	if(ix<=0x413921fb) { /* |x| ~<= 2^19*(pi/2), medium size */
	    t  = fd_fabs(x);
	    n  = (int) (t*invpio2+half);
	    fn = (double)n;
	    r  = t-fn*pio2_1;
	    w  = fn*pio2_1t;	/* 1st round good to 85 bit */
	    if(n<32&&ix!=npio2_hw[n-1]) {	
		y[0] = r-w;	/* quick check no cancellation */
	    } else {
	        j  = ix>>20;
	        y[0] = r-w; 
	        i = j-(((__HI(y[0]))>>20)&0x7ff);
	        if(i>16) {  /* 2nd iteration needed, good to 118 */
		    t  = r;
		    w  = fn*pio2_2;	
		    r  = t-w;
		    w  = fn*pio2_2t-((t-r)-w);	
		    y[0] = r-w;
		    i = j-(((__HI(y[0]))>>20)&0x7ff);
		    if(i>49)  {	/* 3rd iteration need, 151 bits acc */
		    	t  = r;	/* will cover all possible cases */
		    	w  = fn*pio2_3;	
		    	r  = t-w;
		    	w  = fn*pio2_3t-((t-r)-w);	
		    	y[0] = r-w;
		    }
		}
	    }
	    y[1] = (r-y[0])-w;
	    if(hx<0) 	{y[0] = -y[0]; y[1] = -y[1]; return -n;}
	    else	 return n;
	}
    /* 
     * all other (large) arguments
     */
	if(ix>=0x7ff00000) {		/* x is inf or NaN */
	    y[0]=y[1]=x-x; return 0;
	}
    /* set z = scalbn(|x|,ilogb(x)-23) */
	__LO(z) = __LO(x);
	e0 	= (ix>>20)-1046;	/* e0 = ilogb(z)-23; */
	__HI(z) = ix - (e0<<20);
	for(i=0;i<2;i++) {
		tx[i] = (double)((int)(z));
		z     = (z-tx[i])*two24;
	}
	tx[2] = z;
	nx = 3;
	while(tx[nx-1]==zero) nx--;	/* skip zero term */
	n  =  __kernel_rem_pio2(tx,y,e0,nx,2,two_over_pi);
	if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;}
	return n;
}

**** End of e_rem_pio2.c. ****

**** Start of e_scalb.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_scalb.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * __ieee754_scalb(x, fn) is provide for
 * passing various standard test suite. One 
 * should use scalbn() instead.
 */

#include "fdlibm.h"

#ifdef _SCALB_INT
#ifdef __STDC__
	double __ieee754_scalb(double x, int fn)
#else
	double __ieee754_scalb(x,fn)
	double x; int fn;
#endif
#else
#ifdef __STDC__
	double __ieee754_scalb(double x, double fn)
#else
	double __ieee754_scalb(x,fn)
	double x, fn;
#endif
#endif
{
#ifdef _SCALB_INT
	return fd_scalbn(x,fn);
#else
	if (fd_isnan(x)||fd_isnan(fn)) return x*fn;
	if (!fd_finite(fn)) {
	    if(fn>0.0) return x*fn;
	    else       return x/(-fn);
	}
	if (fd_rint(fn)!=fn) return (fn-fn)/(fn-fn);
	if ( fn > 65000.0) return fd_scalbn(x, 65000);
	if (-fn > 65000.0) return fd_scalbn(x,-65000);
	return fd_scalbn(x,(int)fn);
#endif
}

**** End of e_scalb.c. ****

**** Start of e_sinh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)e_sinh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_sinh(x)
 * Method : 
 * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2
 *	1. Replace x by |x| (sinh(-x) = -sinh(x)). 
 *	2. 
 *		                                    E + E/(E+1)
 *	    0        <= x <= 22     :  sinh(x) := --------------, E=expm1(x)
 *			       			        2
 *
 *	    22       <= x <= lnovft :  sinh(x) := exp(x)/2 
 *	    lnovft   <= x <= ln2ovft:  sinh(x) := exp(x/2)/2 * exp(x/2)
 *	    ln2ovft  <  x	    :  sinh(x) := x*shuge (overflow)
 *
 * Special cases:
 *	sinh(x) is |x| if x is +INF, -INF, or NaN.
 *	only sinh(0)=0 is exact for finite x.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double one = 1.0, shuge = 1.0e307;
#else
static double one = 1.0, shuge = 1.0e307;
#endif

#ifdef __STDC__
	double __ieee754_sinh(double x)
#else
	double __ieee754_sinh(x)
	double x;
#endif
{	
	double t,w,h;
	int ix,jx;
	unsigned lx;

    /* High word of |x|. */
	jx = __HI(x);
	ix = jx&0x7fffffff;

    /* x is INF or NaN */
	if(ix>=0x7ff00000) return x+x;	

	h = 0.5;
	if (jx<0) h = -h;
    /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */
	if (ix < 0x40360000) {		/* |x|<22 */
	    if (ix<0x3e300000) 		/* |x|<2**-28 */
		if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */
	    t = fd_expm1(fd_fabs(x));
	    if(ix<0x3ff00000) return h*(2.0*t-t*t/(t+one));
	    return h*(t+t/(t+one));
	}

    /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */
	if (ix < 0x40862E42)  return h*__ieee754_exp(fd_fabs(x));

    /* |x| in [log(maxdouble), overflowthresold] */
	lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x);
	if (ix<0x408633CE || (ix==0x408633ce)&&(lx<=(unsigned)0x8fb9f87d)) {
	    w = __ieee754_exp(0.5*fd_fabs(x));
	    t = h*w;
	    return t*w;
	}

    /* |x| > overflowthresold, sinh(x) overflow */
	return x*shuge;
}

**** End of e_sinh.c. ****

**** Start of e_sqrt.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */
/* @(#)e_sqrt.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __ieee754_sqrt(x)
 * Return correctly rounded sqrt.
 *           ------------------------------------------
 *	     |  Use the hardware sqrt if you have one |
 *           ------------------------------------------
 * Method: 
 *   Bit by bit method using integer arithmetic. (Slow, but portable) 
 *   1. Normalization
 *	Scale x to y in [1,4) with even powers of 2: 
 *	find an integer k such that  1 <= (y=x*2^(2k)) < 4, then
 *		sqrt(x) = 2^k * sqrt(y)
 *   2. Bit by bit computation
 *	Let q  = sqrt(y) truncated to i bit after binary point (q = 1),
 *	     i							 0
 *                                     i+1         2
 *	    s  = 2*q , and	y  =  2   * ( y - q  ).		(1)
 *	     i      i            i                 i
 *                                                        
 *	To compute q    from q , one checks whether 
 *		    i+1       i                       
 *
 *			      -(i+1) 2
 *			(q + 2      ) <= y.			(2)
 *     			  i
 *							      -(i+1)
 *	If (2) is false, then q   = q ; otherwise q   = q  + 2      .
 *		 	       i+1   i             i+1   i
 *
 *	With some algebric manipulation, it is not difficult to see
 *	that (2) is equivalent to 
 *                             -(i+1)
 *			s  +  2       <= y			(3)
 *			 i                i
 *
 *	The advantage of (3) is that s  and y  can be computed by 
 *				      i      i
 *	the following recurrence formula:
 *	    if (3) is false
 *
 *	    s     =  s  ,	y    = y   ;			(4)
 *	     i+1      i		 i+1    i
 *
 *	    otherwise,
 *                         -i                     -(i+1)
 *	    s	  =  s  + 2  ,  y    = y  -  s  - 2  		(5)
 *           i+1      i          i+1    i     i
 *				
 *	One may easily use induction to prove (4) and (5). 
 *	Note. Since the left hand side of (3) contain only i+2 bits,
 *	      it does not necessary to do a full (53-bit) comparison 
 *	      in (3).
 *   3. Final rounding
 *	After generating the 53 bits result, we compute one more bit.
 *	Together with the remainder, we can decide whether the
 *	result is exact, bigger than 1/2ulp, or less than 1/2ulp
 *	(it will never equal to 1/2ulp).
 *	The rounding mode can be detected by checking whether
 *	huge + tiny is equal to huge, and whether huge - tiny is
 *	equal to huge for some floating point number "huge" and "tiny".
 *		
 * Special cases:
 *	sqrt(+-0) = +-0 	... exact
 *	sqrt(inf) = inf
 *	sqrt(-ve) = NaN		... with invalid signal
 *	sqrt(NaN) = NaN		... with invalid signal for signaling NaN
 *
 * Other methods : see the appended file at the end of the program below.
 *---------------
 */

#include "fdlibm.h"

#ifdef __STDC__
static	const double	one	= 1.0, tiny=1.0e-300;
#else
static	double	one	= 1.0, tiny=1.0e-300;
#endif

#ifdef __STDC__
	double __ieee754_sqrt(double x)
#else
	double __ieee754_sqrt(x)
	double x;
#endif
{
	double z;
	int 	sign = (int)0x80000000; 
	unsigned r,t1,s1,ix1,q1;
	int ix0,s0,q,m,t,i;

	ix0 = __HI(x);			/* high word of x */
	ix1 = __LO(x);		/* low word of x */

    /* take care of Inf and NaN */
	if((ix0&0x7ff00000)==0x7ff00000) {			
	    return x*x+x;		/* sqrt(NaN)=NaN, sqrt(+inf)=+inf
					   sqrt(-inf)=sNaN */
	} 
    /* take care of zero */
	if(ix0<=0) {
	    if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */
	    else if(ix0<0)
		return (x-x)/(x-x);		/* sqrt(-ve) = sNaN */
	}
    /* normalize x */
	m = (ix0>>20);
	if(m==0) {				/* subnormal x */
	    while(ix0==0) {
		m -= 21;
		ix0 |= (ix1>>11); ix1 <<= 21;
	    }
	    for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1;
	    m -= i-1;
	    ix0 |= (ix1>>(32-i));
	    ix1 <<= i;
	}
	m -= 1023;	/* unbias exponent */
	ix0 = (ix0&0x000fffff)|0x00100000;
	if(m&1){	/* odd m, double x to make it even */
	    ix0 += ix0 + ((ix1&sign)>>31);
	    ix1 += ix1;
	}
	m >>= 1;	/* m = [m/2] */

    /* generate sqrt(x) bit by bit */
	ix0 += ix0 + ((ix1&sign)>>31);
	ix1 += ix1;
	q = q1 = s0 = s1 = 0;	/* [q,q1] = sqrt(x) */
	r = 0x00200000;		/* r = moving bit from right to left */

	while(r!=0) {
	    t = s0+r; 
	    if(t<=ix0) { 
		s0   = t+r; 
		ix0 -= t; 
		q   += r; 
	    } 
	    ix0 += ix0 + ((ix1&sign)>>31);
	    ix1 += ix1;
	    r>>=1;
	}

	r = sign;
	while(r!=0) {
	    t1 = s1+r; 
	    t  = s0;
	    if((t<ix0)||((t==ix0)&&(t1<=ix1))) { 
		s1  = t1+r;
		if(((int)(t1&sign)==sign)&&(s1&sign)==0) s0 += 1;
		ix0 -= t;
		if (ix1 < t1) ix0 -= 1;
		ix1 -= t1;
		q1  += r;
	    }
	    ix0 += ix0 + ((ix1&sign)>>31);
	    ix1 += ix1;
	    r>>=1;
	}

    /* use floating add to find out rounding direction */
	if((ix0|ix1)!=0) {
	    z = one-tiny; /* trigger inexact flag */
	    if (z>=one) {
	        z = one+tiny;
	        if (q1==(unsigned)0xffffffff) { q1=0; q += 1;}
		else if (z>one) {
		    if (q1==(unsigned)0xfffffffe) q+=1;
		    q1+=2; 
		} else
	            q1 += (q1&1);
	    }
	}
	ix0 = (q>>1)+0x3fe00000;
	ix1 =  q1>>1;
	if ((q&1)==1) ix1 |= sign;
	ix0 += (m <<20);
	__HI(z) = ix0;
	__LO(z) = ix1;
	return z;
}

/*
Other methods  (use floating-point arithmetic)
-------------
(This is a copy of a drafted paper by Prof W. Kahan 
and K.C. Ng, written in May, 1986)

	Two algorithms are given here to implement sqrt(x) 
	(IEEE double precision arithmetic) in software.
	Both supply sqrt(x) correctly rounded. The first algorithm (in
	Section A) uses newton iterations and involves four divisions.
	The second one uses reciproot iterations to avoid division, but
	requires more multiplications. Both algorithms need the ability
	to chop results of arithmetic operations instead of round them, 
	and the INEXACT flag to indicate when an arithmetic operation
	is executed exactly with no roundoff error, all part of the 
	standard (IEEE 754-1985). The ability to perform shift, add,
	subtract and logical AND operations upon 32-bit words is needed
	too, though not part of the standard.

A.  sqrt(x) by Newton Iteration

   (1)	Initial approximation

	Let x0 and x1 be the leading and the trailing 32-bit words of
	a floating point number x (in IEEE double format) respectively 

	    1    11		     52				  ...widths
	   ------------------------------------------------------
	x: |s|	  e     |	      f				|
	   ------------------------------------------------------
	      msb    lsb  msb				      lsb ...order

 
	     ------------------------  	     ------------------------
	x0:  |s|   e    |    f1     |	 x1: |          f2           |
	     ------------------------  	     ------------------------

	By performing shifts and subtracts on x0 and x1 (both regarded
	as integers), we obtain an 8-bit approximation of sqrt(x) as
	follows.

		k  := (x0>>1) + 0x1ff80000;
		y0 := k - T1[31&(k>>15)].	... y ~ sqrt(x) to 8 bits
	Here k is a 32-bit integer and T1[] is an integer array containing
	correction terms. Now magically the floating value of y (y's
	leading 32-bit word is y0, the value of its trailing word is 0)
	approximates sqrt(x) to almost 8-bit.

	Value of T1:
	static int T1[32]= {
	0,	1024,	3062,	5746,	9193,	13348,	18162,	23592,
	29598,	36145,	43202,	50740,	58733,	67158,	75992,	85215,
	83599,	71378,	60428,	50647,	41945,	34246,	27478,	21581,
	16499,	12183,	8588,	5674,	3403,	1742,	661,	130,};

    (2)	Iterative refinement

	Apply Heron's rule three times to y, we have y approximates 
	sqrt(x) to within 1 ulp (Unit in the Last Place):

		y := (y+x/y)/2		... almost 17 sig. bits
		y := (y+x/y)/2		... almost 35 sig. bits
		y := y-(y-x/y)/2	... within 1 ulp


	Remark 1.
	    Another way to improve y to within 1 ulp is:

		y := (y+x/y)		... almost 17 sig. bits to 2*sqrt(x)
		y := y - 0x00100006	... almost 18 sig. bits to sqrt(x)

				2
			    (x-y )*y
		y := y + 2* ----------	...within 1 ulp
			       2
			     3y  + x


	This formula has one division fewer than the one above; however,
	it requires more multiplications and additions. Also x must be
	scaled in advance to avoid spurious overflow in evaluating the
	expression 3y*y+x. Hence it is not recommended uless division
	is slow. If division is very slow, then one should use the 
	reciproot algorithm given in section B.

    (3) Final adjustment

	By twiddling y's last bit it is possible to force y to be 
	correctly rounded according to the prevailing rounding mode
	as follows. Let r and i be copies of the rounding mode and
	inexact flag before entering the square root program. Also we
	use the expression y+-ulp for the next representable floating
	numbers (up and down) of y. Note that y+-ulp = either fixed
	point y+-1, or multiply y by nextafter(1,+-inf) in chopped
	mode.

		I := FALSE;	... reset INEXACT flag I
		R := RZ;	... set rounding mode to round-toward-zero
		z := x/y;	... chopped quotient, possibly inexact
		If(not I) then {	... if the quotient is exact
		    if(z=y) {
		        I := i;	 ... restore inexact flag
		        R := r;  ... restore rounded mode
		        return sqrt(x):=y.
		    } else {
			z := z - ulp;	... special rounding
		    }
		}
		i := TRUE;		... sqrt(x) is inexact
		If (r=RN) then z=z+ulp	... rounded-to-nearest
		If (r=RP) then {	... round-toward-+inf
		    y = y+ulp; z=z+ulp;
		}
		y := y+z;		... chopped sum
		y0:=y0-0x00100000;	... y := y/2 is correctly rounded.
	        I := i;	 		... restore inexact flag
	        R := r;  		... restore rounded mode
	        return sqrt(x):=y.
		    
    (4)	Special cases

	Square root of +inf, +-0, or NaN is itself;
	Square root of a negative number is NaN with invalid signal.


B.  sqrt(x) by Reciproot Iteration

   (1)	Initial approximation

	Let x0 and x1 be the leading and the trailing 32-bit words of
	a floating point number x (in IEEE double format) respectively
	(see section A). By performing shifs and subtracts on x0 and y0,
	we obtain a 7.8-bit approximation of 1/sqrt(x) as follows.

	    k := 0x5fe80000 - (x0>>1);
	    y0:= k - T2[63&(k>>14)].	... y ~ 1/sqrt(x) to 7.8 bits

	Here k is a 32-bit integer and T2[] is an integer array 
	containing correction terms. Now magically the floating
	value of y (y's leading 32-bit word is y0, the value of
	its trailing word y1 is set to zero) approximates 1/sqrt(x)
	to almost 7.8-bit.

	Value of T2:
	static int T2[64]= {
	0x1500,	0x2ef8,	0x4d67,	0x6b02,	0x87be,	0xa395,	0xbe7a,	0xd866,
	0xf14a,	0x1091b,0x11fcd,0x13552,0x14999,0x15c98,0x16e34,0x17e5f,
	0x18d03,0x19a01,0x1a545,0x1ae8a,0x1b5c4,0x1bb01,0x1bfde,0x1c28d,
	0x1c2de,0x1c0db,0x1ba73,0x1b11c,0x1a4b5,0x1953d,0x18266,0x16be0,
	0x1683e,0x179d8,0x18a4d,0x19992,0x1a789,0x1b445,0x1bf61,0x1c989,
	0x1d16d,0x1d77b,0x1dddf,0x1e2ad,0x1e5bf,0x1e6e8,0x1e654,0x1e3cd,
	0x1df2a,0x1d635,0x1cb16,0x1be2c,0x1ae4e,0x19bde,0x1868e,0x16e2e,
	0x1527f,0x1334a,0x11051,0xe951,	0xbe01,	0x8e0d,	0x5924,	0x1edd,};

    (2)	Iterative refinement

	Apply Reciproot iteration three times to y and multiply the
	result by x to get an approximation z that matches sqrt(x)
	to about 1 ulp. To be exact, we will have 
		-1ulp < sqrt(x)-z<1.0625ulp.
	
	... set rounding mode to Round-to-nearest
	   y := y*(1.5-0.5*x*y*y)	... almost 15 sig. bits to 1/sqrt(x)
	   y := y*((1.5-2^-30)+0.5*x*y*y)... about 29 sig. bits to 1/sqrt(x)
	... special arrangement for better accuracy
	   z := x*y			... 29 bits to sqrt(x), with z*y<1
	   z := z + 0.5*z*(1-z*y)	... about 1 ulp to sqrt(x)

	Remark 2. The constant 1.5-2^-30 is chosen to bias the error so that
	(a) the term z*y in the final iteration is always less than 1; 
	(b) the error in the final result is biased upward so that
		-1 ulp < sqrt(x) - z < 1.0625 ulp
	    instead of |sqrt(x)-z|<1.03125ulp.

    (3)	Final adjustment

	By twiddling y's last bit it is possible to force y to be 
	correctly rounded according to the prevailing rounding mode
	as follows. Let r and i be copies of the rounding mode and
	inexact flag before entering the square root program. Also we
	use the expression y+-ulp for the next representable floating
	numbers (up and down) of y. Note that y+-ulp = either fixed
	point y+-1, or multiply y by nextafter(1,+-inf) in chopped
	mode.

	R := RZ;		... set rounding mode to round-toward-zero
	switch(r) {
	    case RN:		... round-to-nearest
	       if(x<= z*(z-ulp)...chopped) z = z - ulp; else
	       if(x<= z*(z+ulp)...chopped) z = z; else z = z+ulp;
	       break;
	    case RZ:case RM:	... round-to-zero or round-to--inf
	       R:=RP;		... reset rounding mod to round-to-+inf
	       if(x<z*z ... rounded up) z = z - ulp; else
	       if(x>=(z+ulp)*(z+ulp) ...rounded up) z = z+ulp;
	       break;
	    case RP:		... round-to-+inf
	       if(x>(z+ulp)*(z+ulp)...chopped) z = z+2*ulp; else
	       if(x>z*z ...chopped) z = z+ulp;
	       break;
	}

	Remark 3. The above comparisons can be done in fixed point. For
	example, to compare x and w=z*z chopped, it suffices to compare
	x1 and w1 (the trailing parts of x and w), regarding them as
	two's complement integers.

	...Is z an exact square root?
	To determine whether z is an exact square root of x, let z1 be the
	trailing part of z, and also let x0 and x1 be the leading and
	trailing parts of x.

	If ((z1&0x03ffffff)!=0)	... not exact if trailing 26 bits of z!=0
	    I := 1;		... Raise Inexact flag: z is not exact
	else {
	    j := 1 - [(x0>>20)&1]	... j = logb(x) mod 2
	    k := z1 >> 26;		... get z's 25-th and 26-th 
					    fraction bits
	    I := i or (k&j) or ((k&(j+j+1))!=(x1&3));
	}
	R:= r		... restore rounded mode
	return sqrt(x):=z.

	If multiplication is cheaper then the foregoing red tape, the 
	Inexact flag can be evaluated by

	    I := i;
	    I := (z*z!=x) or I.

	Note that z*z can overwrite I; this value must be sensed if it is 
	True.

	Remark 4. If z*z = x exactly, then bit 25 to bit 0 of z1 must be
	zero.

		    --------------------
		z1: |        f2        | 
		    --------------------
		bit 31		   bit 0

	Further more, bit 27 and 26 of z1, bit 0 and 1 of x1, and the odd
	or even of logb(x) have the following relations:

	-------------------------------------------------
	bit 27,26 of z1		bit 1,0 of x1	logb(x)
	-------------------------------------------------
	00			00		odd and even
	01			01		even
	10			10		odd
	10			00		even
	11			01		even
	-------------------------------------------------

    (4)	Special cases (see (4) of Section A).	
 
 */
 

**** End of e_sqrt.c. ****

**** Start of fdlibm.h. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)fdlibm.h 1.5 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */


/* Modified defines start here.. */
#ifdef _WIN32
#define huge myhuge
#define __LITTLE_ENDIAN
// disable the "divide by zero" error
#pragma warning ( disable : 4723 )
#endif

/* End here. The rest is the standard file. */

#ifdef __NEWVALID	/* special setup for Sun test regime */
#if defined(i386) || defined(i486) || \
	defined(intel) || defined(x86) || defined(i86pc)
#define __LITTLE_ENDIAN
#endif
#endif


#ifdef __LITTLE_ENDIAN
#define __HI(x) *(1+(int*)&x)
#define __LO(x) *(int*)&x
#define __HIp(x) *(1+(int*)x)
#define __LOp(x) *(int*)x
#else
#define __HI(x) *(int*)&x
#define __LO(x) *(1+(int*)&x)
#define __HIp(x) *(int*)x
#define __LOp(x) *(1+(int*)x)
#endif

#ifdef __STDC__
#define	__P(p)	p
#else
#define	__P(p)	()
#endif

/*
 * ANSI/POSIX
 */

extern int signgam;

#define	MAXFLOAT	((float)3.40282346638528860e+38)

enum fdversion {fdlibm_ieee = -1, fdlibm_svid, fdlibm_xopen, fdlibm_posix};

#define _LIB_VERSION_TYPE enum fdversion
#define _LIB_VERSION _fdlib_version  

/* if global variable _LIB_VERSION is not desirable, one may 
 * change the following to be a constant by: 
 *	#define _LIB_VERSION_TYPE const enum version
 * In that case, after one initializes the value _LIB_VERSION (see
 * s_lib_version.c) during compile time, it cannot be modified
 * in the middle of a program
 */ 
extern  _LIB_VERSION_TYPE  _LIB_VERSION;

#define _IEEE_  fdlibm_ieee
#define _SVID_  fdlibm_svid
#define _XOPEN_ fdlibm_xopen
#define _POSIX_ fdlibm_posix

struct exception {
	int type;
	char *name;
	double arg1;
	double arg2;
	double retval;
};

#define	HUGE		MAXFLOAT

/* 
 * set X_TLOSS = pi*2**52, which is possibly defined in <values.h>
 * (one may replace the following line by "#include <values.h>")
 */

#define X_TLOSS		1.41484755040568800000e+16 

#define	DOMAIN		1
#define	SING		2
#define	OVERFLOW	3
#define	UNDERFLOW	4
#define	TLOSS		5
#define	PLOSS		6

/*
 * ANSI/POSIX
 */

extern double fd_acos __P((double));
extern double fd_asin __P((double));
extern double fd_atan __P((double));
extern double fd_atan2 __P((double, double));
extern double fd_cos __P((double));
extern double fd_sin __P((double));
extern double fd_tan __P((double));
 
extern double fd_cosh __P((double));
extern double fd_sinh __P((double));
extern double fd_tanh __P((double));

extern double fd_exp __P((double));
extern double fd_frexp __P((double, int *));
extern double fd_ldexp __P((double, int));
extern double fd_log __P((double));
extern double fd_log10 __P((double));
extern double fd_modf __P((double, double *));

extern double fd_pow __P((double, double));
extern double fd_sqrt __P((double));

extern double fd_ceil __P((double));
extern double fd_fabs __P((double));
extern double fd_floor __P((double));
extern double fd_fmod __P((double, double));

extern double fd_erf __P((double));
extern double fd_erfc __P((double));
extern double fd_gamma __P((double));
extern double fd_hypot __P((double, double));
extern int fd_isnan __P((double));
extern int fd_finite __P((double));
extern double fd_j0 __P((double));
extern double fd_j1 __P((double));
extern double fd_jn __P((int, double));
extern double fd_lgamma __P((double));
extern double fd_y0 __P((double));
extern double fd_y1 __P((double));
extern double fd_yn __P((int, double));

extern double fd_acosh __P((double));
extern double fd_asinh __P((double));
extern double fd_atanh __P((double));
extern double fd_cbrt __P((double));
extern double fd_logb __P((double));
extern double fd_nextafter __P((double, double));
extern double fd_remainder __P((double, double));
#ifdef _SCALB_INT
extern double fd_scalb __P((double, int));
#else
extern double fd_scalb __P((double, double));
#endif

extern int fd_matherr __P((struct exception *));

/*
 * IEEE Test Vector
 */
extern double significand __P((double));

/*
 * Functions callable from C, intended to support IEEE arithmetic.
 */
extern double fd_copysign __P((double, double));
extern int fd_ilogb __P((double));
extern double fd_rint __P((double));
extern double fd_scalbn __P((double, int));

/*
 * BSD math library entry points
 */
extern double fd_expm1 __P((double));
extern double fd_log1p __P((double));

/*
 * Reentrant version of gamma & lgamma; passes signgam back by reference
 * as the second argument; user must allocate space for signgam.
 */
#ifdef _REENTRANT
extern double gamma_r __P((double, int *));
extern double lgamma_r __P((double, int *));
#endif	/* _REENTRANT */

/* ieee style elementary functions */
extern double __ieee754_sqrt __P((double));			
extern double __ieee754_acos __P((double));			
extern double __ieee754_acosh __P((double));			
extern double __ieee754_log __P((double));			
extern double __ieee754_atanh __P((double));			
extern double __ieee754_asin __P((double));			
extern double __ieee754_atan2 __P((double,double));			
extern double __ieee754_exp __P((double));
extern double __ieee754_cosh __P((double));
extern double __ieee754_fmod __P((double,double));
extern double __ieee754_pow __P((double,double));
extern double __ieee754_lgamma_r __P((double,int *));
extern double __ieee754_gamma_r __P((double,int *));
extern double __ieee754_lgamma __P((double));
extern double __ieee754_gamma __P((double));
extern double __ieee754_log10 __P((double));
extern double __ieee754_sinh __P((double));
extern double __ieee754_hypot __P((double,double));
extern double __ieee754_j0 __P((double));
extern double __ieee754_j1 __P((double));
extern double __ieee754_y0 __P((double));
extern double __ieee754_y1 __P((double));
extern double __ieee754_jn __P((int,double));
extern double __ieee754_yn __P((int,double));
extern double __ieee754_remainder __P((double,double));
extern int    __ieee754_rem_pio2 __P((double,double*));
#ifdef _SCALB_INT
extern double __ieee754_scalb __P((double,int));
#else
extern double __ieee754_scalb __P((double,double));
#endif

/* fdlibm kernel function */
extern double __kernel_standard __P((double,double,int));	
extern double __kernel_sin __P((double,double,int));
extern double __kernel_cos __P((double,double));
extern double __kernel_tan __P((double,double,int));
extern int    __kernel_rem_pio2 __P((double*,double*,int,int,int,const int*));

**** End of fdlibm.h. ****

**** Start of k_cos.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)k_cos.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * __kernel_cos( x,  y )
 * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164
 * Input x is assumed to be bounded by ~pi/4 in magnitude.
 * Input y is the tail of x. 
 *
 * Algorithm
 *	1. Since cos(-x) = cos(x), we need only to consider positive x.
 *	2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0.
 *	3. cos(x) is approximated by a polynomial of degree 14 on
 *	   [0,pi/4]
 *		  	                 4            14
 *	   	cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x
 *	   where the remez error is
 *	
 * 	|              2     4     6     8     10    12     14 |     -58
 * 	|cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x  +C6*x  )| <= 2
 * 	|    					               | 
 * 
 * 	               4     6     8     10    12     14 
 *	4. let r = C1*x +C2*x +C3*x +C4*x +C5*x  +C6*x  , then
 *	       cos(x) = 1 - x*x/2 + r
 *	   since cos(x+y) ~ cos(x) - sin(x)*y 
 *			  ~ cos(x) - x*y,
 *	   a correction term is necessary in cos(x) and hence
 *		cos(x+y) = 1 - (x*x/2 - (r - x*y))
 *	   For better accuracy when x > 0.3, let qx = |x|/4 with
 *	   the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125.
 *	   Then
 *		cos(x+y) = (1-qx) - ((x*x/2-qx) - (r-x*y)).
 *	   Note that 1-qx and (x*x/2-qx) is EXACT here, and the
 *	   magnitude of the latter is at least a quarter of x*x/2,
 *	   thus, reducing the rounding error in the subtraction.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
one =  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
C1  =  4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */
C2  = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */
C3  =  2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */
C4  = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */
C5  =  2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */
C6  = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */

#ifdef __STDC__
	double __kernel_cos(double x, double y)
#else
	double __kernel_cos(x, y)
	double x,y;
#endif
{
	double a,hz,z,r,qx;
	int ix;
	ix = __HI(x)&0x7fffffff;	/* ix = |x|'s high word*/
	if(ix<0x3e400000) {			/* if x < 2**27 */
	    if(((int)x)==0) return one;		/* generate inexact */
	}
	z  = x*x;
	r  = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6)))));
	if(ix < 0x3FD33333) 			/* if |x| < 0.3 */ 
	    return one - (0.5*z - (z*r - x*y));
	else {
	    if(ix > 0x3fe90000) {		/* x > 0.78125 */
		qx = 0.28125;
	    } else {
	        __HI(qx) = ix-0x00200000;	/* x/4 */
	        __LO(qx) = 0;
	    }
	    hz = 0.5*z-qx;
	    a  = one-qx;
	    return a - (hz - (z*r-x*y));
	}
}

**** End of k_cos.c. ****

**** Start of k_rem_pio2.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)k_rem_pio2.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * __kernel_rem_pio2(x,y,e0,nx,prec,ipio2)
 * double x[],y[]; int e0,nx,prec; int ipio2[];
 * 
 * __kernel_rem_pio2 return the last three digits of N with 
 *		y = x - N*pi/2
 * so that |y| < pi/2.
 *
 * The method is to compute the integer (mod 8) and fraction parts of 
 * (2/pi)*x without doing the full multiplication. In general we
 * skip the part of the product that are known to be a huge integer (
 * more accurately, = 0 mod 8 ). Thus the number of operations are
 * independent of the exponent of the input.
 *
 * (2/pi) is represented by an array of 24-bit integers in ipio2[].
 *
 * Input parameters:
 * 	x[]	The input value (must be positive) is broken into nx 
 *		pieces of 24-bit integers in double precision format.
 *		x[i] will be the i-th 24 bit of x. The scaled exponent 
 *		of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 
 *		match x's up to 24 bits.
 *
 *		Example of breaking a double positive z into x[0]+x[1]+x[2]:
 *			e0 = ilogb(z)-23
 *			z  = scalbn(z,-e0)
 *		for i = 0,1,2
 *			x[i] = floor(z)
 *			z    = (z-x[i])*2**24
 *
 *
 *	y[]	ouput result in an array of double precision numbers.
 *		The dimension of y[] is:
 *			24-bit  precision	1
 *			53-bit  precision	2
 *			64-bit  precision	2
 *			113-bit precision	3
 *		The actual value is the sum of them. Thus for 113-bit
 *		precison, one may have to do something like:
 *
 *		long double t,w,r_head, r_tail;
 *		t = (long double)y[2] + (long double)y[1];
 *		w = (long double)y[0];
 *		r_head = t+w;
 *		r_tail = w - (r_head - t);
 *
 *	e0	The exponent of x[0]
 *
 *	nx	dimension of x[]
 *
 *  	prec	an integer indicating the precision:
 *			0	24  bits (single)
 *			1	53  bits (double)
 *			2	64  bits (extended)
 *			3	113 bits (quad)
 *
 *	ipio2[]
 *		integer array, contains the (24*i)-th to (24*i+23)-th 
 *		bit of 2/pi after binary point. The corresponding 
 *		floating value is
 *
 *			ipio2[i] * 2^(-24(i+1)).
 *
 * External function:
 *	double scalbn(), floor();
 *
 *
 * Here is the description of some local variables:
 *
 * 	jk	jk+1 is the initial number of terms of ipio2[] needed
 *		in the computation. The recommended value is 2,3,4,
 *		6 for single, double, extended,and quad.
 *
 * 	jz	local integer variable indicating the number of 
 *		terms of ipio2[] used. 
 *
 *	jx	nx - 1
 *
 *	jv	index for pointing to the suitable ipio2[] for the
 *		computation. In general, we want
 *			( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8
 *		is an integer. Thus
 *			e0-3-24*jv >= 0 or (e0-3)/24 >= jv
 *		Hence jv = max(0,(e0-3)/24).
 *
 *	jp	jp+1 is the number of terms in PIo2[] needed, jp = jk.
 *
 * 	q[]	double array with integral value, representing the
 *		24-bits chunk of the product of x and 2/pi.
 *
 *	q0	the corresponding exponent of q[0]. Note that the
 *		exponent for q[i] would be q0-24*i.
 *
 *	PIo2[]	double precision array, obtained by cutting pi/2
 *		into 24 bits chunks. 
 *
 *	f[]	ipio2[] in floating point 
 *
 *	iq[]	integer array by breaking up q[] in 24-bits chunk.
 *
 *	fq[]	final product of x*(2/pi) in fq[0],..,fq[jk]
 *
 *	ih	integer. If >0 it indicates q[] is >= 0.5, hence
 *		it also indicates the *sign* of the result.
 *
 */


/*
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const int init_jk[] = {2,3,4,6}; /* initial value for jk */
#else
static int init_jk[] = {2,3,4,6}; 
#endif

#ifdef __STDC__
static const double PIo2[] = {
#else
static double PIo2[] = {
#endif
  1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */
  7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */
  5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */
  3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */
  1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */
  1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */
  2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */
  2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */
};

#ifdef __STDC__
static const double			
#else
static double			
#endif
zero   = 0.0,
one    = 1.0,
two24   =  1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */
twon24  =  5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */

#ifdef __STDC__
	int __kernel_rem_pio2(double *x, double *y, int e0, int nx, int prec, const int *ipio2) 
#else
	int __kernel_rem_pio2(x,y,e0,nx,prec,ipio2) 	
	double x[], y[]; int e0,nx,prec; int ipio2[];
#endif
{
	int jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih;
	double z,fw,f[20],fq[20],q[20];

    /* initialize jk*/
	jk = init_jk[prec];
	jp = jk;

    /* determine jx,jv,q0, note that 3>q0 */
	jx =  nx-1;
	jv = (e0-3)/24; if(jv<0) jv=0;
	q0 =  e0-24*(jv+1);

    /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */
	j = jv-jx; m = jx+jk;
	for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (double) ipio2[j];

    /* compute q[0],q[1],...q[jk] */
	for (i=0;i<=jk;i++) {
	    for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; q[i] = fw;
	}

	jz = jk;
recompute:
    /* distill q[] into iq[] reversingly */
	for(i=0,j=jz,z=q[jz];j>0;i++,j--) {
	    fw    =  (double)((int)(twon24* z));
	    iq[i] =  (int)(z-two24*fw);
	    z     =  q[j-1]+fw;
	}

    /* compute n */
	z  = fd_scalbn(z,q0);		/* actual value of z */
	z -= 8.0*fd_floor(z*0.125);		/* trim off integer >= 8 */
	n  = (int) z;
	z -= (double)n;
	ih = 0;
	if(q0>0) {	/* need iq[jz-1] to determine n */
	    i  = (iq[jz-1]>>(24-q0)); n += i;
	    iq[jz-1] -= i<<(24-q0);
	    ih = iq[jz-1]>>(23-q0);
	} 
	else if(q0==0) ih = iq[jz-1]>>23;
	else if(z>=0.5) ih=2;

	if(ih>0) {	/* q > 0.5 */
	    n += 1; carry = 0;
	    for(i=0;i<jz ;i++) {	/* compute 1-q */
		j = iq[i];
		if(carry==0) {
		    if(j!=0) {
			carry = 1; iq[i] = 0x1000000- j;
		    }
		} else  iq[i] = 0xffffff - j;
	    }
	    if(q0>0) {		/* rare case: chance is 1 in 12 */
	        switch(q0) {
	        case 1:
	    	   iq[jz-1] &= 0x7fffff; break;
	    	case 2:
	    	   iq[jz-1] &= 0x3fffff; break;
	        }
	    }
	    if(ih==2) {
		z = one - z;
		if(carry!=0) z -= fd_scalbn(one,q0);
	    }
	}

    /* check if recomputation is needed */
	if(z==zero) {
	    j = 0;
	    for (i=jz-1;i>=jk;i--) j |= iq[i];
	    if(j==0) { /* need recomputation */
		for(k=1;iq[jk-k]==0;k++);   /* k = no. of terms needed */

		for(i=jz+1;i<=jz+k;i++) {   /* add q[jz+1] to q[jz+k] */
		    f[jx+i] = (double) ipio2[jv+i];
		    for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j];
		    q[i] = fw;
		}
		jz += k;
		goto recompute;
	    }
	}

    /* chop off zero terms */
	if(z==0.0) {
	    jz -= 1; q0 -= 24;
	    while(iq[jz]==0) { jz--; q0-=24;}
	} else { /* break z into 24-bit if necessary */
	    z = fd_scalbn(z,-q0);
	    if(z>=two24) { 
		fw = (double)((int)(twon24*z));
		iq[jz] = (int)(z-two24*fw);
		jz += 1; q0 += 24;
		iq[jz] = (int) fw;
	    } else iq[jz] = (int) z ;
	}

    /* convert integer "bit" chunk to floating-point value */
	fw = fd_scalbn(one,q0);
	for(i=jz;i>=0;i--) {
	    q[i] = fw*(double)iq[i]; fw*=twon24;
	}

    /* compute PIo2[0,...,jp]*q[jz,...,0] */
	for(i=jz;i>=0;i--) {
	    for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k];
	    fq[jz-i] = fw;
	}

    /* compress fq[] into y[] */
	switch(prec) {
	    case 0:
		fw = 0.0;
		for (i=jz;i>=0;i--) fw += fq[i];
		y[0] = (ih==0)? fw: -fw; 
		break;
	    case 1:
	    case 2:
		fw = 0.0;
		for (i=jz;i>=0;i--) fw += fq[i]; 
		y[0] = (ih==0)? fw: -fw; 
		fw = fq[0]-fw;
		for (i=1;i<=jz;i++) fw += fq[i];
		y[1] = (ih==0)? fw: -fw; 
		break;
	    case 3:	/* painful */
		for (i=jz;i>0;i--) {
		    fw      = fq[i-1]+fq[i]; 
		    fq[i]  += fq[i-1]-fw;
		    fq[i-1] = fw;
		}
		for (i=jz;i>1;i--) {
		    fw      = fq[i-1]+fq[i]; 
		    fq[i]  += fq[i-1]-fw;
		    fq[i-1] = fw;
		}
		for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; 
		if(ih==0) {
		    y[0] =  fq[0]; y[1] =  fq[1]; y[2] =  fw;
		} else {
		    y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw;
		}
	}
	return n&7;
}

**** End of k_rem_pio2.c. ****

**** Start of k_sin.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)k_sin.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __kernel_sin( x, y, iy)
 * kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854
 * Input x is assumed to be bounded by ~pi/4 in magnitude.
 * Input y is the tail of x.
 * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). 
 *
 * Algorithm
 *	1. Since sin(-x) = -sin(x), we need only to consider positive x. 
 *	2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0.
 *	3. sin(x) is approximated by a polynomial of degree 13 on
 *	   [0,pi/4]
 *		  	         3            13
 *	   	sin(x) ~ x + S1*x + ... + S6*x
 *	   where
 *	
 * 	|sin(x)         2     4     6     8     10     12  |     -58
 * 	|----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x  +S6*x   )| <= 2
 * 	|  x 					           | 
 * 
 *	4. sin(x+y) = sin(x) + sin'(x')*y
 *		    ~ sin(x) + (1-x*x/2)*y
 *	   For better accuracy, let 
 *		     3      2      2      2      2
 *		r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6))))
 *	   then                   3    2
 *		sin(x) = x + (S1*x + (x *(r-y/2)+y))
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
half =  5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */
S1  = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */
S2  =  8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */
S3  = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */
S4  =  2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */
S5  = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */
S6  =  1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */

#ifdef __STDC__
	double __kernel_sin(double x, double y, int iy)
#else
	double __kernel_sin(x, y, iy)
	double x,y; int iy;		/* iy=0 if y is zero */
#endif
{
	double z,r,v;
	int ix;
	ix = __HI(x)&0x7fffffff;	/* high word of x */
	if(ix<0x3e400000)			/* |x| < 2**-27 */
	   {if((int)x==0) return x;}		/* generate inexact */
	z	=  x*x;
	v	=  z*x;
	r	=  S2+z*(S3+z*(S4+z*(S5+z*S6)));
	if(iy==0) return x+v*(S1+z*r);
	else      return x-((z*(half*y-v*r)-y)-v*S1);
}

**** End of k_sin.c. ****

**** Start of k_standard.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)k_standard.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

#include "fdlibm.h"

/* XXX ugly hack to get msvc to link without error. */
#if _LIB_VERSION == _IEEE_
   int errno;
#  define EDOM 0
#  define ERANGE 0
#else
#  include <errno.h>
#endif


#ifndef _USE_WRITE
#include <stdio.h>			/* fputs(), stderr */
#define	WRITE2(u,v)	fputs(u, stderr)
#else	/* !defined(_USE_WRITE) */
#include <unistd.h>			/* write */
#define	WRITE2(u,v)	write(2, u, v)
#undef fflush
#endif	/* !defined(_USE_WRITE) */

static double zero = 0.0;	/* used as const */

/* 
 * Standard conformance (non-IEEE) on exception cases.
 * Mapping:
 *	1 -- acos(|x|>1)
 *	2 -- asin(|x|>1)
 *	3 -- atan2(+-0,+-0)
 *	4 -- hypot overflow
 *	5 -- cosh overflow
 *	6 -- exp overflow
 *	7 -- exp underflow
 *	8 -- y0(0)
 *	9 -- y0(-ve)
 *	10-- y1(0)
 *	11-- y1(-ve)
 *	12-- yn(0)
 *	13-- yn(-ve)
 *	14-- lgamma(finite) overflow
 *	15-- lgamma(-integer)
 *	16-- log(0)
 *	17-- log(x<0)
 *	18-- log10(0)
 *	19-- log10(x<0)
 *	20-- pow(0.0,0.0)
 *	21-- pow(x,y) overflow
 *	22-- pow(x,y) underflow
 *	23-- pow(0,negative) 
 *	24-- pow(neg,non-integral)
 *	25-- sinh(finite) overflow
 *	26-- sqrt(negative)
 *      27-- fmod(x,0)
 *      28-- remainder(x,0)
 *	29-- acosh(x<1)
 *	30-- atanh(|x|>1)
 *	31-- atanh(|x|=1)
 *	32-- scalb overflow
 *	33-- scalb underflow
 *	34-- j0(|x|>X_TLOSS)
 *	35-- y0(x>X_TLOSS)
 *	36-- j1(|x|>X_TLOSS)
 *	37-- y1(x>X_TLOSS)
 *	38-- jn(|x|>X_TLOSS, n)
 *	39-- yn(x>X_TLOSS, n)
 *	40-- gamma(finite) overflow
 *	41-- gamma(-integer)
 *	42-- pow(NaN,0.0)
 */


#ifdef __STDC__
	double __kernel_standard(double x, double y, int type) 
#else
	double __kernel_standard(x,y,type) 
	double x,y; int type;
#endif
{
	struct exception exc;
#ifndef HUGE_VAL	/* this is the only routine that uses HUGE_VAL */ 
#define HUGE_VAL inf
	double inf = 0.0;

	__HI(inf) = 0x7ff00000;	/* set inf to infinite */
#endif

#ifdef _USE_WRITE
	(void) fflush(stdout);
#endif
	exc.arg1 = x;
	exc.arg2 = y;
	switch(type) {
	    case 1:
		/* acos(|x|>1) */
		exc.type = DOMAIN;
		exc.name = "acos";
		exc.retval = zero;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if(_LIB_VERSION == _SVID_) {
		    (void) WRITE2("acos: DOMAIN error\n", 19);
		  }
		  errno = EDOM;
		}
		break;
	    case 2:
		/* asin(|x|>1) */
		exc.type = DOMAIN;
		exc.name = "asin";
		exc.retval = zero;
		if(_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if(_LIB_VERSION == _SVID_) {
		    	(void) WRITE2("asin: DOMAIN error\n", 19);
		  }
		  errno = EDOM;
		}
		break;
	    case 3:
		/* atan2(+-0,+-0) */
		exc.arg1 = y;
		exc.arg2 = x;
		exc.type = DOMAIN;
		exc.name = "atan2";
		exc.retval = zero;
		if(_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if(_LIB_VERSION == _SVID_) {
			(void) WRITE2("atan2: DOMAIN error\n", 20);
		      }
		  errno = EDOM;
		}
		break;
	    case 4:
		/* hypot(finite,finite) overflow */
		exc.type = OVERFLOW;
		exc.name = "hypot";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = HUGE;
		else
		  exc.retval = HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 5:
		/* cosh(finite) overflow */
		exc.type = OVERFLOW;
		exc.name = "cosh";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = HUGE;
		else
		  exc.retval = HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 6:
		/* exp(finite) overflow */
		exc.type = OVERFLOW;
		exc.name = "exp";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = HUGE;
		else
		  exc.retval = HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 7:
		/* exp(finite) underflow */
		exc.type = UNDERFLOW;
		exc.name = "exp";
		exc.retval = zero;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 8:
		/* y0(0) = -inf */
		exc.type = DOMAIN;	/* should be SING for IEEE */
		exc.name = "y0";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("y0: DOMAIN error\n", 17);
		      }
		  errno = EDOM;
		}
		break;
	    case 9:
		/* y0(x<0) = NaN */
		exc.type = DOMAIN;
		exc.name = "y0";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("y0: DOMAIN error\n", 17);
		      }
		  errno = EDOM;
		}
		break;
	    case 10:
		/* y1(0) = -inf */
		exc.type = DOMAIN;	/* should be SING for IEEE */
		exc.name = "y1";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("y1: DOMAIN error\n", 17);
		      }
		  errno = EDOM;
		}
		break;
	    case 11:
		/* y1(x<0) = NaN */
		exc.type = DOMAIN;
		exc.name = "y1";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("y1: DOMAIN error\n", 17);
		      }
		  errno = EDOM;
		}
		break;
	    case 12:
		/* yn(n,0) = -inf */
		exc.type = DOMAIN;	/* should be SING for IEEE */
		exc.name = "yn";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("yn: DOMAIN error\n", 17);
		      }
		  errno = EDOM;
		}
		break;
	    case 13:
		/* yn(x<0) = NaN */
		exc.type = DOMAIN;
		exc.name = "yn";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("yn: DOMAIN error\n", 17);
		      }
		  errno = EDOM;
		}
		break;
	    case 14:
		/* lgamma(finite) overflow */
		exc.type = OVERFLOW;
		exc.name = "lgamma";
                if (_LIB_VERSION == _SVID_)
                  exc.retval = HUGE;
                else
                  exc.retval = HUGE_VAL;
                if (_LIB_VERSION == _POSIX_)
			errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        errno = ERANGE;
		}
		break;
	    case 15:
		/* lgamma(-integer) or lgamma(0) */
		exc.type = SING;
		exc.name = "lgamma";
                if (_LIB_VERSION == _SVID_)
                  exc.retval = HUGE;
                else
                  exc.retval = HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("lgamma: SING error\n", 19);
		      }
		  errno = EDOM;
		}
		break;
	    case 16:
		/* log(0) */
		exc.type = SING;
		exc.name = "log";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("log: SING error\n", 16);
		      }
		  errno = EDOM;
		}
		break;
	    case 17:
		/* log(x<0) */
		exc.type = DOMAIN;
		exc.name = "log";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("log: DOMAIN error\n", 18);
		      }
		  errno = EDOM;
		}
		break;
	    case 18:
		/* log10(0) */
		exc.type = SING;
		exc.name = "log10";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("log10: SING error\n", 18);
		      }
		  errno = EDOM;
		}
		break;
	    case 19:
		/* log10(x<0) */
		exc.type = DOMAIN;
		exc.name = "log10";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = -HUGE;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("log10: DOMAIN error\n", 20);
		      }
		  errno = EDOM;
		}
		break;
	    case 20:
		/* pow(0.0,0.0) */
		/* error only if _LIB_VERSION == _SVID_ */
		exc.type = DOMAIN;
		exc.name = "pow";
		exc.retval = zero;
		if (_LIB_VERSION != _SVID_) exc.retval = 1.0;
		else if (!fd_matherr(&exc)) {
			(void) WRITE2("pow(0,0): DOMAIN error\n", 23);
			errno = EDOM;
		}
		break;
	    case 21:
		/* pow(x,y) overflow */
		exc.type = OVERFLOW;
		exc.name = "pow";
		if (_LIB_VERSION == _SVID_) {
		  exc.retval = HUGE;
		  y *= 0.5;
		  if(x<zero&&fd_rint(y)!=y) exc.retval = -HUGE;
		} else {
		  exc.retval = HUGE_VAL;
		  y *= 0.5;
		  if(x<zero&&fd_rint(y)!=y) exc.retval = -HUGE_VAL;
		}
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 22:
		/* pow(x,y) underflow */
		exc.type = UNDERFLOW;
		exc.name = "pow";
		exc.retval =  zero;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 23:
		/* 0**neg */
		exc.type = DOMAIN;
		exc.name = "pow";
		if (_LIB_VERSION == _SVID_) 
		  exc.retval = zero;
		else
		  exc.retval = -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("pow(0,neg): DOMAIN error\n", 25);
		      }
		  errno = EDOM;
		}
		break;
	    case 24:
		/* neg**non-integral */
		exc.type = DOMAIN;
		exc.name = "pow";
		if (_LIB_VERSION == _SVID_) 
		    exc.retval = zero;
		else 
		    exc.retval = zero/zero;	/* X/Open allow NaN */
		if (_LIB_VERSION == _POSIX_) 
		   errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("neg**non-integral: DOMAIN error\n", 32);
		      }
		  errno = EDOM;
		}
		break;
	    case 25:
		/* sinh(finite) overflow */
		exc.type = OVERFLOW;
		exc.name = "sinh";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = ( (x>zero) ? HUGE : -HUGE);
		else
		  exc.retval = ( (x>zero) ? HUGE_VAL : -HUGE_VAL);
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 26:
		/* sqrt(x<0) */
		exc.type = DOMAIN;
		exc.name = "sqrt";
		if (_LIB_VERSION == _SVID_)
		  exc.retval = zero;
		else
		  exc.retval = zero/zero;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("sqrt: DOMAIN error\n", 19);
		      }
		  errno = EDOM;
		}
		break;
            case 27:
                /* fmod(x,0) */
                exc.type = DOMAIN;
                exc.name = "fmod";
                if (_LIB_VERSION == _SVID_)
                    exc.retval = x;
		else
		    exc.retval = zero/zero;
                if (_LIB_VERSION == _POSIX_)
                  errno = EDOM;
                else if (!fd_matherr(&exc)) {
                  if (_LIB_VERSION == _SVID_) {
                    (void) WRITE2("fmod:  DOMAIN error\n", 20);
                  }
                  errno = EDOM;
                }
                break;
            case 28:
                /* remainder(x,0) */
                exc.type = DOMAIN;
                exc.name = "remainder";
                exc.retval = zero/zero;
                if (_LIB_VERSION == _POSIX_)
                  errno = EDOM;
                else if (!fd_matherr(&exc)) {
                  if (_LIB_VERSION == _SVID_) {
                    (void) WRITE2("remainder: DOMAIN error\n", 24);
                  }
                  errno = EDOM;
                }
                break;
            case 29:
                /* acosh(x<1) */
                exc.type = DOMAIN;
                exc.name = "acosh";
                exc.retval = zero/zero;
                if (_LIB_VERSION == _POSIX_)
                  errno = EDOM;
                else if (!fd_matherr(&exc)) {
                  if (_LIB_VERSION == _SVID_) {
                    (void) WRITE2("acosh: DOMAIN error\n", 20);
                  }
                  errno = EDOM;
                }
                break;
            case 30:
                /* atanh(|x|>1) */
                exc.type = DOMAIN;
                exc.name = "atanh";
                exc.retval = zero/zero;
                if (_LIB_VERSION == _POSIX_)
                  errno = EDOM;
                else if (!fd_matherr(&exc)) {
                  if (_LIB_VERSION == _SVID_) {
                    (void) WRITE2("atanh: DOMAIN error\n", 20);
                  }
                  errno = EDOM;
                }
                break;
            case 31:
                /* atanh(|x|=1) */
                exc.type = SING;
                exc.name = "atanh";
		exc.retval = x/zero;	/* sign(x)*inf */
                if (_LIB_VERSION == _POSIX_)
                  errno = EDOM;
                else if (!fd_matherr(&exc)) {
                  if (_LIB_VERSION == _SVID_) {
                    (void) WRITE2("atanh: SING error\n", 18);
                  }
                  errno = EDOM;
                }
                break;
	    case 32:
		/* scalb overflow; SVID also returns +-HUGE_VAL */
		exc.type = OVERFLOW;
		exc.name = "scalb";
		exc.retval = x > zero ? HUGE_VAL : -HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 33:
		/* scalb underflow */
		exc.type = UNDERFLOW;
		exc.name = "scalb";
		exc.retval = fd_copysign(zero,x);
		if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
		else if (!fd_matherr(&exc)) {
			errno = ERANGE;
		}
		break;
	    case 34:
		/* j0(|x|>X_TLOSS) */
                exc.type = TLOSS;
                exc.name = "j0";
                exc.retval = zero;
                if (_LIB_VERSION == _POSIX_)
                        errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        if (_LIB_VERSION == _SVID_) {
                                (void) WRITE2(exc.name, 2);
                                (void) WRITE2(": TLOSS error\n", 14);
                        }
                        errno = ERANGE;
                }        
		break;
	    case 35:
		/* y0(x>X_TLOSS) */
                exc.type = TLOSS;
                exc.name = "y0";
                exc.retval = zero;
                if (_LIB_VERSION == _POSIX_)
                        errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        if (_LIB_VERSION == _SVID_) {
                                (void) WRITE2(exc.name, 2);
                                (void) WRITE2(": TLOSS error\n", 14);
                        }
                        errno = ERANGE;
                }        
		break;
	    case 36:
		/* j1(|x|>X_TLOSS) */
                exc.type = TLOSS;
                exc.name = "j1";
                exc.retval = zero;
                if (_LIB_VERSION == _POSIX_)
                        errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        if (_LIB_VERSION == _SVID_) {
                                (void) WRITE2(exc.name, 2);
                                (void) WRITE2(": TLOSS error\n", 14);
                        }
                        errno = ERANGE;
                }        
		break;
	    case 37:
		/* y1(x>X_TLOSS) */
                exc.type = TLOSS;
                exc.name = "y1";
                exc.retval = zero;
                if (_LIB_VERSION == _POSIX_)
                        errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        if (_LIB_VERSION == _SVID_) {
                                (void) WRITE2(exc.name, 2);
                                (void) WRITE2(": TLOSS error\n", 14);
                        }
                        errno = ERANGE;
                }        
		break;
	    case 38:
		/* jn(|x|>X_TLOSS) */
                exc.type = TLOSS;
                exc.name = "jn";
                exc.retval = zero;
                if (_LIB_VERSION == _POSIX_)
                        errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        if (_LIB_VERSION == _SVID_) {
                                (void) WRITE2(exc.name, 2);
                                (void) WRITE2(": TLOSS error\n", 14);
                        }
                        errno = ERANGE;
                }        
		break;
	    case 39:
		/* yn(x>X_TLOSS) */
                exc.type = TLOSS;
                exc.name = "yn";
                exc.retval = zero;
                if (_LIB_VERSION == _POSIX_)
                        errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                        if (_LIB_VERSION == _SVID_) {
                                (void) WRITE2(exc.name, 2);
                                (void) WRITE2(": TLOSS error\n", 14);
                        }
                        errno = ERANGE;
                }        
		break;
	    case 40:
		/* gamma(finite) overflow */
		exc.type = OVERFLOW;
		exc.name = "gamma";
                if (_LIB_VERSION == _SVID_)
                  exc.retval = HUGE;
                else
                  exc.retval = HUGE_VAL;
                if (_LIB_VERSION == _POSIX_)
		  errno = ERANGE;
                else if (!fd_matherr(&exc)) {
                  errno = ERANGE;
                }
		break;
	    case 41:
		/* gamma(-integer) or gamma(0) */
		exc.type = SING;
		exc.name = "gamma";
                if (_LIB_VERSION == _SVID_)
                  exc.retval = HUGE;
                else
                  exc.retval = HUGE_VAL;
		if (_LIB_VERSION == _POSIX_)
		  errno = EDOM;
		else if (!fd_matherr(&exc)) {
		  if (_LIB_VERSION == _SVID_) {
			(void) WRITE2("gamma: SING error\n", 18);
		      }
		  errno = EDOM;
		}
		break;
	    case 42:
		/* pow(NaN,0.0) */
		/* error only if _LIB_VERSION == _SVID_ & _XOPEN_ */
		exc.type = DOMAIN;
		exc.name = "pow";
		exc.retval = x;
		if (_LIB_VERSION == _IEEE_ ||
		    _LIB_VERSION == _POSIX_) exc.retval = 1.0;
		else if (!fd_matherr(&exc)) {
			errno = EDOM;
		}
		break;
	}
	return exc.retval; 
}

**** End of k_standard.c. ****

**** Start of k_tan.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)k_tan.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* __kernel_tan( x, y, k )
 * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854
 * Input x is assumed to be bounded by ~pi/4 in magnitude.
 * Input y is the tail of x.
 * Input k indicates whether tan (if k=1) or 
 * -1/tan (if k= -1) is returned.
 *
 * Algorithm
 *	1. Since tan(-x) = -tan(x), we need only to consider positive x. 
 *	2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0.
 *	3. tan(x) is approximated by a odd polynomial of degree 27 on
 *	   [0,0.67434]
 *		  	         3             27
 *	   	tan(x) ~ x + T1*x + ... + T13*x
 *	   where
 *	
 * 	        |tan(x)         2     4            26   |     -59.2
 * 	        |----- - (1+T1*x +T2*x +.... +T13*x    )| <= 2
 * 	        |  x 					| 
 * 
 *	   Note: tan(x+y) = tan(x) + tan'(x)*y
 *		          ~ tan(x) + (1+x*x)*y
 *	   Therefore, for better accuracy in computing tan(x+y), let 
 *		     3      2      2       2       2
 *		r = x *(T2+x *(T3+x *(...+x *(T12+x *T13))))
 *	   then
 *		 		    3    2
 *		tan(x+y) = x + (T1*x + (x *(r+y)+y))
 *
 *      4. For x in [0.67434,pi/4],  let y = pi/4 - x, then
 *		tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y))
 *		       = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y)))
 */

#include "fdlibm.h"
#ifdef __STDC__
static const double 
#else
static double 
#endif
one   =  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
pio4  =  7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */
pio4lo=  3.06161699786838301793e-17, /* 0x3C81A626, 0x33145C07 */
T[] =  {
  3.33333333333334091986e-01, /* 0x3FD55555, 0x55555563 */
  1.33333333333201242699e-01, /* 0x3FC11111, 0x1110FE7A */
  5.39682539762260521377e-02, /* 0x3FABA1BA, 0x1BB341FE */
  2.18694882948595424599e-02, /* 0x3F9664F4, 0x8406D637 */
  8.86323982359930005737e-03, /* 0x3F8226E3, 0xE96E8493 */
  3.59207910759131235356e-03, /* 0x3F6D6D22, 0xC9560328 */
  1.45620945432529025516e-03, /* 0x3F57DBC8, 0xFEE08315 */
  5.88041240820264096874e-04, /* 0x3F4344D8, 0xF2F26501 */
  2.46463134818469906812e-04, /* 0x3F3026F7, 0x1A8D1068 */
  7.81794442939557092300e-05, /* 0x3F147E88, 0xA03792A6 */
  7.14072491382608190305e-05, /* 0x3F12B80F, 0x32F0A7E9 */
 -1.85586374855275456654e-05, /* 0xBEF375CB, 0xDB605373 */
  2.59073051863633712884e-05, /* 0x3EFB2A70, 0x74BF7AD4 */
};

#ifdef __STDC__
	double __kernel_tan(double x, double y, int iy)
#else
	double __kernel_tan(x, y, iy)
	double x,y; int iy;
#endif
{
	double z,r,v,w,s;
	int ix,hx;
	hx = __HI(x);	/* high word of x */
	ix = hx&0x7fffffff;	/* high word of |x| */
	if(ix<0x3e300000)			/* x < 2**-28 */
	    {if((int)x==0) {			/* generate inexact */
		if(((ix|__LO(x))|(iy+1))==0) return one/fd_fabs(x);
		else return (iy==1)? x: -one/x;
	    }
	    }
	if(ix>=0x3FE59428) { 			/* |x|>=0.6744 */
	    if(hx<0) {x = -x; y = -y;}
	    z = pio4-x;
	    w = pio4lo-y;
	    x = z+w; y = 0.0;
	}
	z	=  x*x;
	w 	=  z*z;
    /* Break x^5*(T[1]+x^2*T[2]+...) into
     *	  x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
     *	  x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
     */
	r = T[1]+w*(T[3]+w*(T[5]+w*(T[7]+w*(T[9]+w*T[11]))));
	v = z*(T[2]+w*(T[4]+w*(T[6]+w*(T[8]+w*(T[10]+w*T[12])))));
	s = z*x;
	r = y + z*(s*(r+v)+y);
	r += T[0]*s;
	w = x+r;
	if(ix>=0x3FE59428) {
	    v = (double)iy;
	    return (double)(1-((hx>>30)&2))*(v-2.0*(x-(w*w/(w+v)-r)));
	}
	if(iy==1) return w;
	else {		/* if allow error up to 2 ulp, 
			   simply return -1.0/(x+r) here */
     /*  compute -1.0/(x+r) accurately */
	    double a,t;
	    z  = w;
	    __LO(z) = 0;
	    v  = r-(z - x); 	/* z+v = r+x */
	    t = a  = -1.0/w;	/* a = -1.0/w */
	    __LO(t) = 0;
	    s  = 1.0+t*z;
	    return t+a*(s+t*v);
	}
}

**** End of k_tan.c. ****

**** Start of s_asinh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_asinh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* asinh(x)
 * Method :
 *	Based on 
 *		asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ]
 *	we have
 *	asinh(x) := x  if  1+x*x=1,
 *		 := sign(x)*(log(x)+ln2)) for large |x|, else
 *		 := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else
 *		 := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2)))  
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double 
#else
static double 
#endif
one =  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
ln2 =  6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */
huge=  1.00000000000000000000e+300; 

#ifdef __STDC__
	double fd_asinh(double x)
#else
	double fd_asinh(x)
	double x;
#endif
{	
	double t,w;
	int hx,ix;
	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x7ff00000) return x+x;	/* x is inf or NaN */
	if(ix< 0x3e300000) {	/* |x|<2**-28 */
	    if(huge+x>one) return x;	/* return x inexact except 0 */
	} 
	if(ix>0x41b00000) {	/* |x| > 2**28 */
	    w = __ieee754_log(fd_fabs(x))+ln2;
	} else if (ix>0x40000000) {	/* 2**28 > |x| > 2.0 */
	    t = fd_fabs(x);
	    w = __ieee754_log(2.0*t+one/(fd_sqrt(x*x+one)+t));
	} else {		/* 2.0 > |x| > 2**-28 */
	    t = x*x;
	    w =fd_log1p(fd_fabs(x)+t/(one+fd_sqrt(one+t)));
	}
	if(hx>0) return w; else return -w;
}

**** End of s_asinh.c. ****

**** Start of s_atan.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_atan.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* atan(x)
 * Method
 *   1. Reduce x to positive by atan(x) = -atan(-x).
 *   2. According to the integer k=4t+0.25 chopped, t=x, the argument
 *      is further reduced to one of the following intervals and the
 *      arctangent of t is evaluated by the corresponding formula:
 *
 *      [0,7/16]      atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
 *      [7/16,11/16]  atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
 *      [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
 *      [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
 *      [39/16,INF]   atan(x) = atan(INF) + atan( -1/t )
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double atanhi[] = {
#else
static double atanhi[] = {
#endif
  4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
  7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
  9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
  1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
};

#ifdef __STDC__
static const double atanlo[] = {
#else
static double atanlo[] = {
#endif
  2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
  3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
  1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
  6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
};

#ifdef __STDC__
static const double aT[] = {
#else
static double aT[] = {
#endif
  3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
 -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
  1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
 -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
  9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
 -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
  6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
 -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
  4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
 -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
  1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
};

#ifdef __STDC__
	static const double 
#else
	static double 
#endif
one   = 1.0,
huge   = 1.0e300;

#ifdef __STDC__
	double fd_atan(double x)
#else
	double fd_atan(x)
	double x;
#endif
{
	double w,s1,s2,z;
	int ix,hx,id;

	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x44100000) {	/* if |x| >= 2^66 */
	    if(ix>0x7ff00000||
		(ix==0x7ff00000&&(__LO(x)!=0)))
		return x+x;		/* NaN */
	    if(hx>0) return  atanhi[3]+atanlo[3];
	    else     return -atanhi[3]-atanlo[3];
	} if (ix < 0x3fdc0000) {	/* |x| < 0.4375 */
	    if (ix < 0x3e200000) {	/* |x| < 2^-29 */
		if(huge+x>one) return x;	/* raise inexact */
	    }
	    id = -1;
	} else {
	x = fd_fabs(x);
	if (ix < 0x3ff30000) {		/* |x| < 1.1875 */
	    if (ix < 0x3fe60000) {	/* 7/16 <=|x|<11/16 */
		id = 0; x = (2.0*x-one)/(2.0+x); 
	    } else {			/* 11/16<=|x|< 19/16 */
		id = 1; x  = (x-one)/(x+one); 
	    }
	} else {
	    if (ix < 0x40038000) {	/* |x| < 2.4375 */
		id = 2; x  = (x-1.5)/(one+1.5*x);
	    } else {			/* 2.4375 <= |x| < 2^66 */
		id = 3; x  = -1.0/x;
	    }
	}}
    /* end of argument reduction */
	z = x*x;
	w = z*z;
    /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
	s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10])))));
	s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9]))));
	if (id<0) return x - x*(s1+s2);
	else {
	    z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x);
	    return (hx<0)? -z:z;
	}
}

**** End of s_atan.c. ****

**** Start of s_cbrt.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_cbrt.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

#include "fdlibm.h"

/* cbrt(x)
 * Return cube root of x
 */
#ifdef __STDC__
static const unsigned 
#else
static unsigned 
#endif
	B1 = 715094163, /* B1 = (682-0.03306235651)*2**20 */
	B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */

#ifdef __STDC__
static const double
#else
static double
#endif
C =  5.42857142857142815906e-01, /* 19/35     = 0x3FE15F15, 0xF15F15F1 */
D = -7.05306122448979611050e-01, /* -864/1225 = 0xBFE691DE, 0x2532C834 */
E =  1.41428571428571436819e+00, /* 99/70     = 0x3FF6A0EA, 0x0EA0EA0F */
F =  1.60714285714285720630e+00, /* 45/28     = 0x3FF9B6DB, 0x6DB6DB6E */
G =  3.57142857142857150787e-01; /* 5/14      = 0x3FD6DB6D, 0xB6DB6DB7 */

#ifdef __STDC__
	double fd_cbrt(double x) 
#else
	double fd_cbrt(x) 
	double x;
#endif
{
	int	hx;
	double r,s,t=0.0,w;
	unsigned sign;


	hx = __HI(x);		/* high word of x */
	sign=hx&0x80000000; 		/* sign= sign(x) */
	hx  ^=sign;
	if(hx>=0x7ff00000) return(x+x); /* cbrt(NaN,INF) is itself */
	if((hx|__LO(x))==0) 
	    return(x);		/* cbrt(0) is itself */

	__HI(x) = hx;	/* x <- |x| */
    /* rough cbrt to 5 bits */
	if(hx<0x00100000) 		/* subnormal number */
	  {__HI(t)=0x43500000; 		/* set t= 2**54 */
	   t*=x; __HI(t)=__HI(t)/3+B2;
	  }
	else
	  __HI(t)=hx/3+B1;	


    /* new cbrt to 23 bits, may be implemented in single precision */
	r=t*t/x;
	s=C+r*t;
	t*=G+F/(s+E+D/s);	

    /* chopped to 20 bits and make it larger than cbrt(x) */ 
	__LO(t)=0; __HI(t)+=0x00000001;


    /* one step newton iteration to 53 bits with error less than 0.667 ulps */
	s=t*t;		/* t*t is exact */
	r=x/s;
	w=t+t;
	r=(r-t)/(w+r);	/* r-s is exact */
	t=t+t*r;

    /* retore the sign bit */
	__HI(t) |= sign;
	return(t);
}

**** End of s_cbrt.c. ****

**** Start of s_ceil.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_ceil.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * ceil(x)
 * Return x rounded toward -inf to integral value
 * Method:
 *	Bit twiddling.
 * Exception:
 *	Inexact flag raised if x not equal to ceil(x).
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double huge = 1.0e300;
#else
static double huge = 1.0e300;
#endif

#ifdef __STDC__
	double fd_ceil(double x)
#else
	double fd_ceil(x)
	double x;
#endif
{
	int i0,i1,j0;
	unsigned i,j;
	i0 =  __HI(x);
	i1 =  __LO(x);
	j0 = ((i0>>20)&0x7ff)-0x3ff;
	if(j0<20) {
	    if(j0<0) { 	/* raise inexact if x != 0 */
		if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */
		    if(i0<0) {i0=0x80000000;i1=0;} 
		    else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;}
		}
	    } else {
		i = (0x000fffff)>>j0;
		if(((i0&i)|i1)==0) return x; /* x is integral */
		if(huge+x>0.0) {	/* raise inexact flag */
		    if(i0>0) i0 += (0x00100000)>>j0;
		    i0 &= (~i); i1=0;
		}
	    }
	} else if (j0>51) {
	    if(j0==0x400) return x+x;	/* inf or NaN */
	    else return x;		/* x is integral */
	} else {
	    i = ((unsigned)(0xffffffff))>>(j0-20);
	    if((i1&i)==0) return x;	/* x is integral */
	    if(huge+x>0.0) { 		/* raise inexact flag */
		if(i0>0) {
		    if(j0==20) i0+=1; 
		    else {
			j = i1 + (1<<(52-j0));
			if((int)j<i1) i0+=1;	/* got a carry */
			i1 = j;
		    }
		}
		i1 &= (~i);
	    }
	}
	__HI(x) = i0;
	__LO(x) = i1;
	return x;
}

**** End of s_ceil.c. ****

**** Start of s_copysign.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_copysign.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * copysign(double x, double y)
 * copysign(x,y) returns a value with the magnitude of x and
 * with the sign bit of y.
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_copysign(double x, double y)
#else
	double fd_copysign(x,y)
	double x,y;
#endif
{
	__HI(x) = (__HI(x)&0x7fffffff)|(__HI(y)&0x80000000);
        return x;
}

**** End of s_copysign.c. ****

**** Start of s_cos.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_cos.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* cos(x)
 * Return cosine function of x.
 *
 * kernel function:
 *	__kernel_sin		... sine function on [-pi/4,pi/4]
 *	__kernel_cos		... cosine function on [-pi/4,pi/4]
 *	__ieee754_rem_pio2	... argument reduction routine
 *
 * Method.
 *      Let S,C and T denote the sin, cos and tan respectively on 
 *	[-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 
 *	in [-pi/4 , +pi/4], and let n = k mod 4.
 *	We have
 *
 *          n        sin(x)      cos(x)        tan(x)
 *     ----------------------------------------------------------
 *	    0	       S	   C		 T
 *	    1	       C	  -S		-1/T
 *	    2	      -S	  -C		 T
 *	    3	      -C	   S		-1/T
 *     ----------------------------------------------------------
 *
 * Special cases:
 *      Let trig be any of sin, cos, or tan.
 *      trig(+-INF)  is NaN, with signals;
 *      trig(NaN)    is that NaN;
 *
 * Accuracy:
 *	TRIG(x) returns trig(x) nearly rounded 
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_cos(double x)
#else
	double fd_cos(x)
	double x;
#endif
{
	double y[2],z=0.0;
	int n, ix;

    /* High word of x. */
	ix = __HI(x);

    /* |x| ~< pi/4 */
	ix &= 0x7fffffff;
	if(ix <= 0x3fe921fb) return __kernel_cos(x,z);

    /* cos(Inf or NaN) is NaN */
	else if (ix>=0x7ff00000) return x-x;

    /* argument reduction needed */
	else {
	    n = __ieee754_rem_pio2(x,y);
	    switch(n&3) {
		case 0: return  __kernel_cos(y[0],y[1]);
		case 1: return -__kernel_sin(y[0],y[1],1);
		case 2: return -__kernel_cos(y[0],y[1]);
		default:
		        return  __kernel_sin(y[0],y[1],1);
	    }
	}
}

**** End of s_cos.c. ****

**** Start of s_erf.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_erf.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* double erf(double x)
 * double erfc(double x)
 *			     x
 *		      2      |\
 *     erf(x)  =  ---------  | exp(-t*t)dt
 *	 	   sqrt(pi) \| 
 *			     0
 *
 *     erfc(x) =  1-erf(x)
 *  Note that 
 *		erf(-x) = -erf(x)
 *		erfc(-x) = 2 - erfc(x)
 *
 * Method:
 *	1. For |x| in [0, 0.84375]
 *	    erf(x)  = x + x*R(x^2)
 *          erfc(x) = 1 - erf(x)           if x in [-.84375,0.25]
 *                  = 0.5 + ((0.5-x)-x*R)  if x in [0.25,0.84375]
 *	   where R = P/Q where P is an odd poly of degree 8 and
 *	   Q is an odd poly of degree 10.
 *						 -57.90
 *			| R - (erf(x)-x)/x | <= 2
 *	
 *
 *	   Remark. The formula is derived by noting
 *          erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....)
 *	   and that
 *          2/sqrt(pi) = 1.128379167095512573896158903121545171688
 *	   is close to one. The interval is chosen because the fix
 *	   point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
 *	   near 0.6174), and by some experiment, 0.84375 is chosen to
 * 	   guarantee the error is less than one ulp for erf.
 *
 *      2. For |x| in [0.84375,1.25], let s = |x| - 1, and
 *         c = 0.84506291151 rounded to single (24 bits)
 *         	erf(x)  = sign(x) * (c  + P1(s)/Q1(s))
 *         	erfc(x) = (1-c)  - P1(s)/Q1(s) if x > 0
 *			  1+(c+P1(s)/Q1(s))    if x < 0
 *         	|P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
 *	   Remark: here we use the taylor series expansion at x=1.
 *		erf(1+s) = erf(1) + s*Poly(s)
 *			 = 0.845.. + P1(s)/Q1(s)
 *	   That is, we use rational approximation to approximate
 *			erf(1+s) - (c = (single)0.84506291151)
 *	   Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
 *	   where 
 *		P1(s) = degree 6 poly in s
 *		Q1(s) = degree 6 poly in s
 *
 *      3. For x in [1.25,1/0.35(~2.857143)], 
 *         	erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
 *         	erf(x)  = 1 - erfc(x)
 *	   where 
 *		R1(z) = degree 7 poly in z, (z=1/x^2)
 *		S1(z) = degree 8 poly in z
 *
 *      4. For x in [1/0.35,28]
 *         	erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
 *			= 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6<x<0
 *			= 2.0 - tiny		(if x <= -6)
 *         	erf(x)  = sign(x)*(1.0 - erfc(x)) if x < 6, else
 *         	erf(x)  = sign(x)*(1.0 - tiny)
 *	   where
 *		R2(z) = degree 6 poly in z, (z=1/x^2)
 *		S2(z) = degree 7 poly in z
 *
 *      Note1:
 *	   To compute exp(-x*x-0.5625+R/S), let s be a single
 *	   precision number and s := x; then
 *		-x*x = -s*s + (s-x)*(s+x)
 *	        exp(-x*x-0.5626+R/S) = 
 *			exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
 *      Note2:
 *	   Here 4 and 5 make use of the asymptotic series
 *			  exp(-x*x)
 *		erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) )
 *			  x*sqrt(pi)
 *	   We use rational approximation to approximate
 *      	g(s)=f(1/x^2) = log(erfc(x)*x) - x*x + 0.5625
 *	   Here is the error bound for R1/S1 and R2/S2
 *      	|R1/S1 - f(x)|  < 2**(-62.57)
 *      	|R2/S2 - f(x)|  < 2**(-61.52)
 *
 *      5. For inf > x >= 28
 *         	erf(x)  = sign(x) *(1 - tiny)  (raise inexact)
 *         	erfc(x) = tiny*tiny (raise underflow) if x > 0
 *			= 2 - tiny if x<0
 *
 *      7. Special case:
 *         	erf(0)  = 0, erf(inf)  = 1, erf(-inf) = -1,
 *         	erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, 
 *	   	erfc/erf(NaN) is NaN
 */


#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
tiny	    = 1e-300,
half=  5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */
one =  1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */
two =  2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */
	/* c = (float)0.84506291151 */
erx =  8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */
/*
 * Coefficients for approximation to  erf on [0,0.84375]
 */
efx =  1.28379167095512586316e-01, /* 0x3FC06EBA, 0x8214DB69 */
efx8=  1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */
pp0  =  1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */
pp1  = -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */
pp2  = -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */
pp3  = -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */
pp4  = -2.37630166566501626084e-05, /* 0xBEF8EAD6, 0x120016AC */
qq1  =  3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */
qq2  =  6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */
qq3  =  5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */
qq4  =  1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */
qq5  = -3.96022827877536812320e-06, /* 0xBED09C43, 0x42A26120 */
/*
 * Coefficients for approximation to  erf  in [0.84375,1.25] 
 */
pa0  = -2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */
pa1  =  4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */
pa2  = -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */
pa3  =  3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */
pa4  = -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */
pa5  =  3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */
pa6  = -2.16637559486879084300e-03, /* 0xBF61BF38, 0x0A96073F */
qa1  =  1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */
qa2  =  5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */
qa3  =  7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */
qa4  =  1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */
qa5  =  1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */
qa6  =  1.19844998467991074170e-02, /* 0x3F888B54, 0x5735151D */
/*
 * Coefficients for approximation to  erfc in [1.25,1/0.35]
 */
ra0  = -9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */
ra1  = -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */
ra2  = -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */
ra3  = -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */
ra4  = -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */
ra5  = -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */
ra6  = -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */
ra7  = -9.81432934416914548592e+00, /* 0xC023A0EF, 0xC69AC25C */
sa1  =  1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */
sa2  =  1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */
sa3  =  4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */
sa4  =  6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */
sa5  =  4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */
sa6  =  1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */
sa7  =  6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */
sa8  = -6.04244152148580987438e-02, /* 0xBFAEEFF2, 0xEE749A62 */
/*
 * Coefficients for approximation to  erfc in [1/.35,28]
 */
rb0  = -9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */
rb1  = -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */
rb2  = -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */
rb3  = -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */
rb4  = -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */
rb5  = -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */
rb6  = -4.83519191608651397019e+02, /* 0xC07E384E, 0x9BDC383F */
sb1  =  3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */
sb2  =  3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */
sb3  =  1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */
sb4  =  3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */
sb5  =  2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */
sb6  =  4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */
sb7  = -2.24409524465858183362e+01; /* 0xC03670E2, 0x42712D62 */

#ifdef __STDC__
	double fd_erf(double x) 
#else
	double fd_erf(x) 
	double x;
#endif
{
	int hx,ix,i;
	double R,S,P,Q,s,y,z,r;
	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x7ff00000) {		/* erf(nan)=nan */
	    i = ((unsigned)hx>>31)<<1;
	    return (double)(1-i)+one/x;	/* erf(+-inf)=+-1 */
	}

	if(ix < 0x3feb0000) {		/* |x|<0.84375 */
	    if(ix < 0x3e300000) { 	/* |x|<2**-28 */
	        if (ix < 0x00800000) 
		    return 0.125*(8.0*x+efx8*x);  /*avoid underflow */
		return x + efx*x;
	    }
	    z = x*x;
	    r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4)));
	    s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))));
	    y = r/s;
	    return x + x*y;
	}
	if(ix < 0x3ff40000) {		/* 0.84375 <= |x| < 1.25 */
	    s = fd_fabs(x)-one;
	    P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))));
	    Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))));
	    if(hx>=0) return erx + P/Q; else return -erx - P/Q;
	}
	if (ix >= 0x40180000) {		/* inf>|x|>=6 */
	    if(hx>=0) return one-tiny; else return tiny-one;
	}
	x = fd_fabs(x);
 	s = one/(x*x);
	if(ix< 0x4006DB6E) {	/* |x| < 1/0.35 */
	    R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(
				ra5+s*(ra6+s*ra7))))));
	    S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(
				sa5+s*(sa6+s*(sa7+s*sa8)))))));
	} else {	/* |x| >= 1/0.35 */
	    R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(
				rb5+s*rb6)))));
	    S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(
				sb5+s*(sb6+s*sb7))))));
	}
	z  = x;  
	__LO(z) = 0;
	r  =  __ieee754_exp(-z*z-0.5625)*__ieee754_exp((z-x)*(z+x)+R/S);
	if(hx>=0) return one-r/x; else return  r/x-one;
}

#ifdef __STDC__
	double erfc(double x) 
#else
	double erfc(x) 
	double x;
#endif
{
	int hx,ix;
	double R,S,P,Q,s,y,z,r;
	hx = __HI(x);
	ix = hx&0x7fffffff;
	if(ix>=0x7ff00000) {			/* erfc(nan)=nan */
						/* erfc(+-inf)=0,2 */
	    return (double)(((unsigned)hx>>31)<<1)+one/x;
	}

	if(ix < 0x3feb0000) {		/* |x|<0.84375 */
	    if(ix < 0x3c700000)  	/* |x|<2**-56 */
		return one-x;
	    z = x*x;
	    r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4)));
	    s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))));
	    y = r/s;
	    if(hx < 0x3fd00000) {  	/* x<1/4 */
		return one-(x+x*y);
	    } else {
		r = x*y;
		r += (x-half);
	        return half - r ;
	    }
	}
	if(ix < 0x3ff40000) {		/* 0.84375 <= |x| < 1.25 */
	    s = fd_fabs(x)-one;
	    P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))));
	    Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))));
	    if(hx>=0) {
	        z  = one-erx; return z - P/Q; 
	    } else {
		z = erx+P/Q; return one+z;
	    }
	}
	if (ix < 0x403c0000) {		/* |x|<28 */
	    x = fd_fabs(x);
 	    s = one/(x*x);
	    if(ix< 0x4006DB6D) {	/* |x| < 1/.35 ~ 2.857143*/
	        R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(
				ra5+s*(ra6+s*ra7))))));
	        S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(
				sa5+s*(sa6+s*(sa7+s*sa8)))))));
	    } else {			/* |x| >= 1/.35 ~ 2.857143 */
		if(hx<0&&ix>=0x40180000) return two-tiny;/* x < -6 */
	        R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(
				rb5+s*rb6)))));
	        S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(
				sb5+s*(sb6+s*sb7))))));
	    }
	    z  = x;
	    __LO(z)  = 0;
	    r  =  __ieee754_exp(-z*z-0.5625)*
			__ieee754_exp((z-x)*(z+x)+R/S);
	    if(hx>0) return r/x; else return two-r/x;
	} else {
	    if(hx>0) return tiny*tiny; else return two-tiny;
	}
}

**** End of s_erf.c. ****

**** Start of s_expm1.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_expm1.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* expm1(x)
 * Returns exp(x)-1, the exponential of x minus 1.
 *
 * Method
 *   1. Argument reduction:
 *	Given x, find r and integer k such that
 *
 *               x = k*ln2 + r,  |r| <= 0.5*ln2 ~ 0.34658  
 *
 *      Here a correction term c will be computed to compensate 
 *	the error in r when rounded to a floating-point number.
 *
 *   2. Approximating expm1(r) by a special rational function on
 *	the interval [0,0.34658]:
 *	Since
 *	    r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ...
 *	we define R1(r*r) by
 *	    r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r)
 *	That is,
 *	    R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r)
 *		     = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r))
 *		     = 1 - r^2/60 + r^4/2520 - r^6/100800 + ...
 *      We use a special Reme algorithm on [0,0.347] to generate 
 * 	a polynomial of degree 5 in r*r to approximate R1. The 
 *	maximum error of this polynomial approximation is bounded 
 *	by 2**-61. In other words,
 *	    R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5
 *	where 	Q1  =  -1.6666666666666567384E-2,
 * 		Q2  =   3.9682539681370365873E-4,
 * 		Q3  =  -9.9206344733435987357E-6,
 * 		Q4  =   2.5051361420808517002E-7,
 * 		Q5  =  -6.2843505682382617102E-9;
 *  	(where z=r*r, and the values of Q1 to Q5 are listed below)
 *	with error bounded by
 *	    |                  5           |     -61
 *	    | 1.0+Q1*z+...+Q5*z   -  R1(z) | <= 2 
 *	    |                              |
 *	
 *	expm1(r) = exp(r)-1 is then computed by the following 
 * 	specific way which minimize the accumulation rounding error: 
 *			       2     3
 *			      r     r    [ 3 - (R1 + R1*r/2)  ]
 *	      expm1(r) = r + --- + --- * [--------------------]
 *		              2     2    [ 6 - r*(3 - R1*r/2) ]
 *	
 *	To compensate the error in the argument reduction, we use
 *		expm1(r+c) = expm1(r) + c + expm1(r)*c 
 *			   ~ expm1(r) + c + r*c 
 *	Thus c+r*c will be added in as the correction terms for
 *	expm1(r+c). Now rearrange the term to avoid optimization 
 * 	screw up:
 *		        (      2                                    2 )
 *		        ({  ( r    [ R1 -  (3 - R1*r/2) ]  )  }    r  )
 *	 expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- )
 *	                ({  ( 2    [ 6 - r*(3 - R1*r/2) ]  )  }    2  )
 *                      (                                             )
 *    	
 *		   = r - E
 *   3. Scale back to obtain expm1(x):
 *	From step 1, we have
 *	   expm1(x) = either 2^k*[expm1(r)+1] - 1
 *		    = or     2^k*[expm1(r) + (1-2^-k)]
 *   4. Implementation notes:
 *	(A). To save one multiplication, we scale the coefficient Qi
 *	     to Qi*2^i, and replace z by (x^2)/2.
 *	(B). To achieve maximum accuracy, we compute expm1(x) by
 *	  (i)   if x < -56*ln2, return -1.0, (raise inexact if x!=inf)
 *	  (ii)  if k=0, return r-E
 *	  (iii) if k=-1, return 0.5*(r-E)-0.5
 *        (iv)	if k=1 if r < -0.25, return 2*((r+0.5)- E)
 *	       	       else	     return  1.0+2.0*(r-E);
 *	  (v)   if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1)
 *	  (vi)  if k <= 20, return 2^k((1-2^-k)-(E-r)), else
 *	  (vii) return 2^k(1-((E+2^-k)-r)) 
 *
 * Special cases:
 *	expm1(INF) is INF, expm1(NaN) is NaN;
 *	expm1(-INF) is -1, and
 *	for finite argument, only expm1(0)=0 is exact.
 *
 * Accuracy:
 *	according to an error analysis, the error is always less than
 *	1 ulp (unit in the last place).
 *
 * Misc. info.
 *	For IEEE double 
 *	    if x >  7.09782712893383973096e+02 then expm1(x) overflow
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough
 * to produce the hexadecimal values shown.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
one		= 1.0,
huge		= 1.0e+300,
tiny		= 1.0e-300,
o_threshold	= 7.09782712893383973096e+02,/* 0x40862E42, 0xFEFA39EF */
ln2_hi		= 6.93147180369123816490e-01,/* 0x3fe62e42, 0xfee00000 */
ln2_lo		= 1.90821492927058770002e-10,/* 0x3dea39ef, 0x35793c76 */
invln2		= 1.44269504088896338700e+00,/* 0x3ff71547, 0x652b82fe */
	/* scaled coefficients related to expm1 */
Q1  =  -3.33333333333331316428e-02, /* BFA11111 111110F4 */
Q2  =   1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */
Q3  =  -7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */
Q4  =   4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */
Q5  =  -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */

#ifdef __STDC__
	double fd_expm1(double x)
#else
	double fd_expm1(x)
	double x;
#endif
{
	double y,hi,lo,c,t,e,hxs,hfx,r1;
	int k,xsb;
	unsigned hx;

	hx  = __HI(x);	/* high word of x */
	xsb = hx&0x80000000;		/* sign bit of x */
	if(xsb==0) y=x; else y= -x;	/* y = |x| */
	hx &= 0x7fffffff;		/* high word of |x| */

    /* filter out huge and non-finite argument */
	if(hx >= 0x4043687A) {			/* if |x|>=56*ln2 */
	    if(hx >= 0x40862E42) {		/* if |x|>=709.78... */
                if(hx>=0x7ff00000) {
		    if(((hx&0xfffff)|__LO(x))!=0) 
		         return x+x; 	 /* NaN */
		    else return (xsb==0)? x:-1.0;/* exp(+-inf)={inf,-1} */
	        }
	        if(x > o_threshold) return huge*huge; /* overflow */
	    }
	    if(xsb!=0) { /* x < -56*ln2, return -1.0 with inexact */
		if(x+tiny<0.0)		/* raise inexact */
		return tiny-one;	/* return -1 */
	    }
	}

    /* argument reduction */
	if(hx > 0x3fd62e42) {		/* if  |x| > 0.5 ln2 */ 
	    if(hx < 0x3FF0A2B2) {	/* and |x| < 1.5 ln2 */
		if(xsb==0)
		    {hi = x - ln2_hi; lo =  ln2_lo;  k =  1;}
		else
		    {hi = x + ln2_hi; lo = -ln2_lo;  k = -1;}
	    } else {
		k  = (int)(invln2*x+((xsb==0)?0.5:-0.5));
		t  = k;
		hi = x - t*ln2_hi;	/* t*ln2_hi is exact here */
		lo = t*ln2_lo;
	    }
	    x  = hi - lo;
	    c  = (hi-x)-lo;
	} 
	else if(hx < 0x3c900000) {  	/* when |x|<2**-54, return x */
	    t = huge+x;	/* return x with inexact flags when x!=0 */
	    return x - (t-(huge+x));	
	}
	else k = 0;

    /* x is now in primary range */
	hfx = 0.5*x;
	hxs = x*hfx;
	r1 = one+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5))));
	t  = 3.0-r1*hfx;
	e  = hxs*((r1-t)/(6.0 - x*t));
	if(k==0) return x - (x*e-hxs);		/* c is 0 */
	else {
	    e  = (x*(e-c)-c);
	    e -= hxs;
	    if(k== -1) return 0.5*(x-e)-0.5;
	    if(k==1) 
	       	if(x < -0.25) return -2.0*(e-(x+0.5));
	       	else 	      return  one+2.0*(x-e);
	    if (k <= -2 || k>56) {   /* suffice to return exp(x)-1 */
	        y = one-(e-x);
	        __HI(y) += (k<<20);	/* add k to y's exponent */
	        return y-one;
	    }
	    t = one;
	    if(k<20) {
	       	__HI(t) = 0x3ff00000 - (0x200000>>k);  /* t=1-2^-k */
	       	y = t-(e-x);
	       	__HI(y) += (k<<20);	/* add k to y's exponent */
	   } else {
	       	__HI(t)  = ((0x3ff-k)<<20);	/* 2^-k */
	       	y = x-(e+t);
	       	y += one;
	       	__HI(y) += (k<<20);	/* add k to y's exponent */
	    }
	}
	return y;
}

**** End of s_expm1.c. ****

**** Start of s_fabs.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_fabs.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * fabs(x) returns the absolute value of x.
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_fabs(double x)
#else
	double fd_fabs(x)
	double x;
#endif
{
	__HI(x) &= 0x7fffffff;
        return x;
}

**** End of s_fabs.c. ****

**** Start of s_finite.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_finite.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * finite(x) returns 1 is x is finite, else 0;
 * no branching!
 */

#include "fdlibm.h"

#ifdef __STDC__
	int fd_finite(double x)
#else
	int fd_finite(x)
	double x;
#endif
{
	int hx; 
	hx = __HI(x);
	return  (unsigned)((hx&0x7fffffff)-0x7ff00000)>>31;
}

**** End of s_finite.c. ****

**** Start of s_floor.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_floor.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * floor(x)
 * Return x rounded toward -inf to integral value
 * Method:
 *	Bit twiddling.
 * Exception:
 *	Inexact flag raised if x not equal to floor(x).
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double huge = 1.0e300;
#else
static double huge = 1.0e300;
#endif

#ifdef __STDC__
	double fd_floor(double x)
#else
	double fd_floor(x)
	double x;
#endif
{
	int i0,i1,j0;
	unsigned i,j;
	i0 =  __HI(x);
	i1 =  __LO(x);
	j0 = ((i0>>20)&0x7ff)-0x3ff;
	if(j0<20) {
	    if(j0<0) { 	/* raise inexact if x != 0 */
		if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */
		    if(i0>=0) {i0=i1=0;} 
		    else if(((i0&0x7fffffff)|i1)!=0)
			{ i0=0xbff00000;i1=0;}
		}
	    } else {
		i = (0x000fffff)>>j0;
		if(((i0&i)|i1)==0) return x; /* x is integral */
		if(huge+x>0.0) {	/* raise inexact flag */
		    if(i0<0) i0 += (0x00100000)>>j0;
		    i0 &= (~i); i1=0;
		}
	    }
	} else if (j0>51) {
	    if(j0==0x400) return x+x;	/* inf or NaN */
	    else return x;		/* x is integral */
	} else {
	    i = ((unsigned)(0xffffffff))>>(j0-20);
	    if((i1&i)==0) return x;	/* x is integral */
	    if(huge+x>0.0) { 		/* raise inexact flag */
		if(i0<0) {
		    if(j0==20) i0+=1; 
		    else {
			j = i1+(1<<(52-j0));
			if((int)j<i1) i0 +=1 ; 	/* got a carry */
			i1=j;
		    }
		}
		i1 &= (~i);
	    }
	}
	__HI(x) = i0;
	__LO(x) = i1;
	return x;
}

**** End of s_floor.c. ****

**** Start of s_frexp.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_frexp.c 1.4 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * for non-zero x 
 *	x = frexp(arg,&exp);
 * return a double fp quantity x such that 0.5 <= |x| <1.0
 * and the corresponding binary exponent "exp". That is
 *	arg = x*2^exp.
 * If arg is inf, 0.0, or NaN, then frexp(arg,&exp) returns arg 
 * with *exp=0. 
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
two54 =  1.80143985094819840000e+16; /* 0x43500000, 0x00000000 */

#ifdef __STDC__
	double fd_frexp(double x, int *eptr)
#else
	double fd_frexp(x, eptr)
	double x; int *eptr;
#endif
{
	int  hx, ix, lx;
	hx = __HI(x);
	ix = 0x7fffffff&hx;
	lx = __LO(x);
	*eptr = 0;
	if(ix>=0x7ff00000||((ix|lx)==0)) return x;	/* 0,inf,nan */
	if (ix<0x00100000) {		/* subnormal */
	    x *= two54;
	    hx = __HI(x);
	    ix = hx&0x7fffffff;
	    *eptr = -54;
	}
	*eptr += (ix>>20)-1022;
	hx = (hx&0x800fffff)|0x3fe00000;
	__HI(x) = hx;
	return x;
}

**** End of s_frexp.c. ****

**** Start of s_ilogb.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_ilogb.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* ilogb(double x)
 * return the binary exponent of non-zero x
 * ilogb(0) = 0x80000001
 * ilogb(inf/NaN) = 0x7fffffff (no signal is raised)
 */

#include "fdlibm.h"

#ifdef __STDC__
	int fd_ilogb(double x)
#else
	int fd_ilogb(x)
	double x;
#endif
{
	int hx,lx,ix;

	hx  = (__HI(x))&0x7fffffff;	/* high word of x */
	if(hx<0x00100000) {
	    lx = __LO(x);
	    if((hx|lx)==0) 
		return 0x80000001;	/* ilogb(0) = 0x80000001 */
	    else			/* subnormal x */
		if(hx==0) {
		    for (ix = -1043; lx>0; lx<<=1) ix -=1;
		} else {
		    for (ix = -1022,hx<<=11; hx>0; hx<<=1) ix -=1;
		}
	    return ix;
	}
	else if (hx<0x7ff00000) return (hx>>20)-1023;
	else return 0x7fffffff;
}

**** End of s_ilogb.c. ****

**** Start of s_isnan.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_isnan.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * isnan(x) returns 1 is x is nan, else 0;
 * no branching!
 */

#include "fdlibm.h"

#ifdef __STDC__
	int fd_isnan(double x)
#else
	int fd_isnan(x)
	double x;
#endif
{
	int hx,lx;
	hx = (__HI(x)&0x7fffffff);
	lx = __LO(x);
	hx |= (unsigned)(lx|(-lx))>>31;	
	hx = 0x7ff00000 - hx;
	return ((unsigned)(hx))>>31;
}

**** End of s_isnan.c. ****

**** Start of s_ldexp.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_ldexp.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

#include "fdlibm.h"
#include <errno.h>

#ifdef __STDC__
	double fd_ldexp(double value, int exp)
#else
	double fd_ldexp(value, exp)
	double value; int exp;
#endif
{
	if(!fd_finite(value)||value==0.0) return value;
	value = fd_scalbn(value,exp);
	if(!fd_finite(value)||value==0.0) errno = ERANGE;
	return value;
}

**** End of s_ldexp.c. ****

**** Start of s_lib_version.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_lib_version.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * MACRO for standards
 */

#include "fdlibm.h"

/*
 * define and initialize _LIB_VERSION
 */
#ifdef _POSIX_MODE
_LIB_VERSION_TYPE _LIB_VERSION = _POSIX_;
#else
#ifdef _XOPEN_MODE
_LIB_VERSION_TYPE _LIB_VERSION = _XOPEN_;
#else
#ifdef _SVID3_MODE
_LIB_VERSION_TYPE _LIB_VERSION = _SVID_;
#else					/* default _IEEE_MODE */
_LIB_VERSION_TYPE _LIB_VERSION = _IEEE_;
#endif
#endif
#endif

**** End of s_lib_version.c. ****

**** Start of s_log1p.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_log1p.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* double log1p(double x)
 *
 * Method :                  
 *   1. Argument Reduction: find k and f such that 
 *			1+x = 2^k * (1+f), 
 *	   where  sqrt(2)/2 < 1+f < sqrt(2) .
 *
 *      Note. If k=0, then f=x is exact. However, if k!=0, then f
 *	may not be representable exactly. In that case, a correction
 *	term is need. Let u=1+x rounded. Let c = (1+x)-u, then
 *	log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
 *	and add back the correction term c/u.
 *	(Note: when x > 2**53, one can simply return log(x))
 *
 *   2. Approximation of log1p(f).
 *	Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
 *		 = 2s + 2/3 s**3 + 2/5 s**5 + .....,
 *	     	 = 2s + s*R
 *      We use a special Reme algorithm on [0,0.1716] to generate 
 * 	a polynomial of degree 14 to approximate R The maximum error 
 *	of this polynomial approximation is bounded by 2**-58.45. In
 *	other words,
 *		        2      4      6      8      10      12      14
 *	    R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
 *  	(the values of Lp1 to Lp7 are listed in the program)
 *	and
 *	    |      2          14          |     -58.45
 *	    | Lp1*s +...+Lp7*s    -  R(z) | <= 2 
 *	    |                             |
 *	Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
 *	In order to guarantee error in log below 1ulp, we compute log
 *	by
 *		log1p(f) = f - (hfsq - s*(hfsq+R)).
 *	
 *	3. Finally, log1p(x) = k*ln2 + log1p(f).  
 *		 	     = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
 *	   Here ln2 is split into two floating point number: 
 *			ln2_hi + ln2_lo,
 *	   where n*ln2_hi is always exact for |n| < 2000.
 *
 * Special cases:
 *	log1p(x) is NaN with signal if x < -1 (including -INF) ; 
 *	log1p(+INF) is +INF; log1p(-1) is -INF with signal;
 *	log1p(NaN) is that NaN with no signal.
 *
 * Accuracy:
 *	according to an error analysis, the error is always less than
 *	1 ulp (unit in the last place).
 *
 * Constants:
 * The hexadecimal values are the intended ones for the following 
 * constants. The decimal values may be used, provided that the 
 * compiler will convert from decimal to binary accurately enough 
 * to produce the hexadecimal values shown.
 *
 * Note: Assuming log() return accurate answer, the following
 * 	 algorithm can be used to compute log1p(x) to within a few ULP:
 *	
 *		u = 1+x;
 *		if(u==1.0) return x ; else
 *			   return log(u)*(x/(u-1.0));
 *
 *	 See HP-15C Advanced Functions Handbook, p.193.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
ln2_hi  =  6.93147180369123816490e-01,	/* 3fe62e42 fee00000 */
ln2_lo  =  1.90821492927058770002e-10,	/* 3dea39ef 35793c76 */
two54   =  1.80143985094819840000e+16,  /* 43500000 00000000 */
Lp1 = 6.666666666666735130e-01,  /* 3FE55555 55555593 */
Lp2 = 3.999999999940941908e-01,  /* 3FD99999 9997FA04 */
Lp3 = 2.857142874366239149e-01,  /* 3FD24924 94229359 */
Lp4 = 2.222219843214978396e-01,  /* 3FCC71C5 1D8E78AF */
Lp5 = 1.818357216161805012e-01,  /* 3FC74664 96CB03DE */
Lp6 = 1.531383769920937332e-01,  /* 3FC39A09 D078C69F */
Lp7 = 1.479819860511658591e-01;  /* 3FC2F112 DF3E5244 */

static double zero = 0.0;

#ifdef __STDC__
	double fd_log1p(double x)
#else
	double fd_log1p(x)
	double x;
#endif
{
	double hfsq,f,c,s,z,R,u;
	int k,hx,hu,ax;

	hx = __HI(x);		/* high word of x */
	ax = hx&0x7fffffff;

	k = 1;
	if (hx < 0x3FDA827A) {			/* x < 0.41422  */
	    if(ax>=0x3ff00000) {		/* x <= -1.0 */
		if(x==-1.0) return -two54/zero; /* log1p(-1)=+inf */
		else return (x-x)/(x-x);	/* log1p(x<-1)=NaN */
	    }
	    if(ax<0x3e200000) {			/* |x| < 2**-29 */
		if(two54+x>zero			/* raise inexact */
	            &&ax<0x3c900000) 		/* |x| < 2**-54 */
		    return x;
		else
		    return x - x*x*0.5;
	    }
	    if(hx>0||hx<=((int)0xbfd2bec3)) {
		k=0;f=x;hu=1;}	/* -0.2929<x<0.41422 */
	} 
	if (hx >= 0x7ff00000) return x+x;
	if(k!=0) {
	    if(hx<0x43400000) {
		u  = 1.0+x; 
	        hu = __HI(u);		/* high word of u */
	        k  = (hu>>20)-1023;
	        c  = (k>0)? 1.0-(u-x):x-(u-1.0);/* correction term */
		c /= u;
	    } else {
		u  = x;
	        hu = __HI(u);		/* high word of u */
	        k  = (hu>>20)-1023;
		c  = 0;
	    }
	    hu &= 0x000fffff;
	    if(hu<0x6a09e) {
	        __HI(u) = hu|0x3ff00000;	/* normalize u */
	    } else {
	        k += 1; 
	        __HI(u) = hu|0x3fe00000;	/* normalize u/2 */
	        hu = (0x00100000-hu)>>2;
	    }
	    f = u-1.0;
	}
	hfsq=0.5*f*f;
	if(hu==0) {	/* |f| < 2**-20 */
	    if(f==zero) if(k==0) return zero;  
			else {c += k*ln2_lo; return k*ln2_hi+c;}
	    R = hfsq*(1.0-0.66666666666666666*f);
	    if(k==0) return f-R; else
	    	     return k*ln2_hi-((R-(k*ln2_lo+c))-f);
	}
 	s = f/(2.0+f); 
	z = s*s;
	R = z*(Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7))))));
	if(k==0) return f-(hfsq-s*(hfsq+R)); else
		 return k*ln2_hi-((hfsq-(s*(hfsq+R)+(k*ln2_lo+c)))-f);
}

**** End of s_log1p.c. ****

**** Start of s_logb.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_logb.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * double logb(x)
 * IEEE 754 logb. Included to pass IEEE test suite. Not recommend.
 * Use ilogb instead.
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_logb(double x)
#else
	double fd_logb(x)
	double x;
#endif
{
	int lx,ix;
	ix = (__HI(x))&0x7fffffff;	/* high |x| */
	lx = __LO(x);			/* low x */
	if((ix|lx)==0) return -1.0/fd_fabs(x);
	if(ix>=0x7ff00000) return x*x;
	if((ix>>=20)==0) 			/* IEEE 754 logb */
		return -1022.0; 
	else
		return (double) (ix-1023); 
}

**** End of s_logb.c. ****

**** Start of s_matherr.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_matherr.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

#include "fdlibm.h"

#ifdef __STDC__
	int fd_matherr(struct exception *x)
#else
	int fd_matherr(x)
	struct exception *x;
#endif
{
	int n=0;
	if(x->arg1!=x->arg1) return 0;
	return n;
}

**** End of s_matherr.c. ****

**** Start of s_modf.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_modf.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * modf(double x, double *iptr) 
 * return fraction part of x, and return x's integral part in *iptr.
 * Method:
 *	Bit twiddling.
 *
 * Exception:
 *	No exception.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double one = 1.0;
#else
static double one = 1.0;
#endif

#ifdef __STDC__
	double fd_modf(double x, double *iptr)
#else
	double fd_modf(x, iptr)
	double x,*iptr;
#endif
{
	int i0,i1,j0;
	unsigned i;
	i0 =  __HI(x);		/* high x */
	i1 =  __LO(x);		/* low  x */
	j0 = ((i0>>20)&0x7ff)-0x3ff;	/* exponent of x */
	if(j0<20) {			/* integer part in high x */
	    if(j0<0) {			/* |x|<1 */
		__HIp(iptr) = i0&0x80000000;
		__LOp(iptr) = 0;		/* *iptr = +-0 */
		return x;
	    } else {
		i = (0x000fffff)>>j0;
		if(((i0&i)|i1)==0) {		/* x is integral */
		    *iptr = x;
		    __HI(x) &= 0x80000000;
		    __LO(x)  = 0;	/* return +-0 */
		    return x;
		} else {
		    __HIp(iptr) = i0&(~i);
		    __LOp(iptr) = 0;
		    return x - *iptr;
		}
	    }
	} else if (j0>51) {		/* no fraction part */
	    *iptr = x*one;
	    __HI(x) &= 0x80000000;
	    __LO(x)  = 0;	/* return +-0 */
	    return x;
	} else {			/* fraction part in low x */
	    i = ((unsigned)(0xffffffff))>>(j0-20);
	    if((i1&i)==0) { 		/* x is integral */
		*iptr = x;
		__HI(x) &= 0x80000000;
		__LO(x)  = 0;	/* return +-0 */
		return x;
	    } else {
		__HIp(iptr) = i0;
		__LOp(iptr) = i1&(~i);
		return x - *iptr;
	    }
	}
}

**** End of s_modf.c. ****

**** Start of s_nextafter.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_nextafter.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* IEEE functions
 *	nextafter(x,y)
 *	return the next machine floating-point number of x in the
 *	direction toward y.
 *   Special cases:
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_nextafter(double x, double y)
#else
	double fd_nextafter(x,y)
	double x,y;
#endif
{
	int	hx,hy,ix,iy;
	unsigned lx,ly;

	hx = __HI(x);		/* high word of x */
	lx = __LO(x);		/* low  word of x */
	hy = __HI(y);		/* high word of y */
	ly = __LO(y);		/* low  word of y */
	ix = hx&0x7fffffff;		/* |x| */
	iy = hy&0x7fffffff;		/* |y| */

	if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) ||   /* x is nan */ 
	   ((iy>=0x7ff00000)&&((iy-0x7ff00000)|ly)!=0))     /* y is nan */ 
	   return x+y;				
	if(x==y) return x;		/* x=y, return x */
	if((ix|lx)==0) {			/* x == 0 */
	    __HI(x) = hy&0x80000000;	/* return +-minsubnormal */
	    __LO(x) = 1;
	    y = x*x;
	    if(y==x) return y; else return x;	/* raise underflow flag */
	} 
	if(hx>=0) {				/* x > 0 */
	    if(hx>hy||((hx==hy)&&(lx>ly))) {	/* x > y, x -= ulp */
		if(lx==0) hx -= 1;
		lx -= 1;
	    } else {				/* x < y, x += ulp */
		lx += 1;
		if(lx==0) hx += 1;
	    }
	} else {				/* x < 0 */
	    if(hy>=0||hx>hy||((hx==hy)&&(lx>ly))){/* x < y, x -= ulp */
		if(lx==0) hx -= 1;
		lx -= 1;
	    } else {				/* x > y, x += ulp */
		lx += 1;
		if(lx==0) hx += 1;
	    }
	}
	hy = hx&0x7ff00000;
	if(hy>=0x7ff00000) return x+x;	/* overflow  */
	if(hy<0x00100000) {		/* underflow */
	    y = x*x;
	    if(y!=x) {		/* raise underflow flag */
		__HI(y) = hx; __LO(y) = lx;
		return y;
	    }
	}
	__HI(x) = hx; __LO(x) = lx;
	return x;
}

**** End of s_nextafter.c. ****

**** Start of s_rint.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_rint.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * rint(x)
 * Return x rounded to integral value according to the prevailing
 * rounding mode.
 * Method:
 *	Using floating addition.
 * Exception:
 *	Inexact flag raised if x not equal to rint(x).
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double 
#endif
TWO52[2]={
  4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */
 -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */
};

#ifdef __STDC__
	double fd_rint(double x)
#else
	double fd_rint(x)
	double x;
#endif
{
	int i0,j0,sx;
	unsigned i,i1;
	double w,t;
	i0 =  __HI(x);
	sx = (i0>>31)&1;
	i1 =  __LO(x);
	j0 = ((i0>>20)&0x7ff)-0x3ff;
	if(j0<20) {
	    if(j0<0) { 	
		if(((i0&0x7fffffff)|i1)==0) return x;
		i1 |= (i0&0x0fffff);
		i0 &= 0xfffe0000;
		i0 |= ((i1|-(int)i1)>>12)&0x80000;
		__HI(x)=i0;
	        w = TWO52[sx]+x;
	        t =  w-TWO52[sx];
	        i0 = __HI(t);
	        __HI(t) = (i0&0x7fffffff)|(sx<<31);
	        return t;
	    } else {
		i = (0x000fffff)>>j0;
		if(((i0&i)|i1)==0) return x; /* x is integral */
		i>>=1;
		if(((i0&i)|i1)!=0) {
		    if(j0==19) i1 = 0x40000000; else
		    i0 = (i0&(~i))|((0x20000)>>j0);
		}
	    }
	} else if (j0>51) {
	    if(j0==0x400) return x+x;	/* inf or NaN */
	    else return x;		/* x is integral */
	} else {
	    i = ((unsigned)(0xffffffff))>>(j0-20);
	    if((i1&i)==0) return x;	/* x is integral */
	    i>>=1;
	    if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20));
	}
	__HI(x) = i0;
	__LO(x) = i1;
	w = TWO52[sx]+x;
	return w-TWO52[sx];
}

**** End of s_rint.c. ****

**** Start of s_scalbn.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_scalbn.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * scalbn (double x, int n)
 * scalbn(x,n) returns x* 2**n  computed by  exponent  
 * manipulation rather than by actually performing an 
 * exponentiation or a multiplication.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
two54   =  1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */
twom54  =  5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */
huge   = 1.0e+300,
tiny   = 1.0e-300;

#ifdef __STDC__
	double fd_scalbn (double x, int n)
#else
	double fd_scalbn (x,n)
	double x; int n;
#endif
{
	int  k,hx,lx;
	hx = __HI(x);
	lx = __LO(x);
        k = (hx&0x7ff00000)>>20;		/* extract exponent */
        if (k==0) {				/* 0 or subnormal x */
            if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */
	    x *= two54; 
	    hx = __HI(x);
	    k = ((hx&0x7ff00000)>>20) - 54; 
            if (n< -50000) return tiny*x; 	/*underflow*/
	    }
        if (k==0x7ff) return x+x;		/* NaN or Inf */
        k = k+n; 
        if (k >  0x7fe) return huge*fd_copysign(huge,x); /* overflow  */
        if (k > 0) 				/* normal result */
	    {__HI(x) = (hx&0x800fffff)|(k<<20); return x;}
        if (k <= -54)
            if (n > 50000) 	/* in case integer overflow in n+k */
		return huge*fd_copysign(huge,x);	/*overflow*/
	    else return tiny*fd_copysign(tiny,x); 	/*underflow*/
        k += 54;				/* subnormal result */
        __HI(x) = (hx&0x800fffff)|(k<<20);
        return x*twom54;
}

**** End of s_scalbn.c. ****

**** Start of s_signgam.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */
#include "fdlibm.h"
int signgam = 0;

**** End of s_signgam.c. ****

**** Start of s_significand.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_significand.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * significand(x) computes just
 * 	scalb(x, (double) -ilogb(x)),
 * for exercising the fraction-part(F) IEEE 754-1985 test vector.
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_significand(double x)
#else
	double fd_significand(x)
	double x;
#endif
{
	return __ieee754_scalb(x,(double) -fd_ilogb(x));
}

**** End of s_significand.c. ****

**** Start of s_sin.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_sin.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* sin(x)
 * Return sine function of x.
 *
 * kernel function:
 *	__kernel_sin		... sine function on [-pi/4,pi/4]
 *	__kernel_cos		... cose function on [-pi/4,pi/4]
 *	__ieee754_rem_pio2	... argument reduction routine
 *
 * Method.
 *      Let S,C and T denote the sin, cos and tan respectively on 
 *	[-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 
 *	in [-pi/4 , +pi/4], and let n = k mod 4.
 *	We have
 *
 *          n        sin(x)      cos(x)        tan(x)
 *     ----------------------------------------------------------
 *	    0	       S	   C		 T
 *	    1	       C	  -S		-1/T
 *	    2	      -S	  -C		 T
 *	    3	      -C	   S		-1/T
 *     ----------------------------------------------------------
 *
 * Special cases:
 *      Let trig be any of sin, cos, or tan.
 *      trig(+-INF)  is NaN, with signals;
 *      trig(NaN)    is that NaN;
 *
 * Accuracy:
 *	TRIG(x) returns trig(x) nearly rounded 
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_sin(double x)
#else
	double fd_sin(x)
	double x;
#endif
{
	double y[2],z=0.0;
	int n, ix;

    /* High word of x. */
	ix = __HI(x);

    /* |x| ~< pi/4 */
	ix &= 0x7fffffff;
	if(ix <= 0x3fe921fb) return __kernel_sin(x,z,0);

    /* sin(Inf or NaN) is NaN */
	else if (ix>=0x7ff00000) return x-x;

    /* argument reduction needed */
	else {
	    n = __ieee754_rem_pio2(x,y);
	    switch(n&3) {
		case 0: return  __kernel_sin(y[0],y[1],1);
		case 1: return  __kernel_cos(y[0],y[1]);
		case 2: return -__kernel_sin(y[0],y[1],1);
		default:
			return -__kernel_cos(y[0],y[1]);
	    }
	}
}

**** End of s_sin.c. ****

**** Start of s_tan.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_tan.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* tan(x)
 * Return tangent function of x.
 *
 * kernel function:
 *	__kernel_tan		... tangent function on [-pi/4,pi/4]
 *	__ieee754_rem_pio2	... argument reduction routine
 *
 * Method.
 *      Let S,C and T denote the sin, cos and tan respectively on 
 *	[-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 
 *	in [-pi/4 , +pi/4], and let n = k mod 4.
 *	We have
 *
 *          n        sin(x)      cos(x)        tan(x)
 *     ----------------------------------------------------------
 *	    0	       S	   C		 T
 *	    1	       C	  -S		-1/T
 *	    2	      -S	  -C		 T
 *	    3	      -C	   S		-1/T
 *     ----------------------------------------------------------
 *
 * Special cases:
 *      Let trig be any of sin, cos, or tan.
 *      trig(+-INF)  is NaN, with signals;
 *      trig(NaN)    is that NaN;
 *
 * Accuracy:
 *	TRIG(x) returns trig(x) nearly rounded 
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_tan(double x)
#else
	double fd_tan(x)
	double x;
#endif
{
	double y[2],z=0.0;
	int n, ix;

    /* High word of x. */
	ix = __HI(x);

    /* |x| ~< pi/4 */
	ix &= 0x7fffffff;
	if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1);

    /* tan(Inf or NaN) is NaN */
	else if (ix>=0x7ff00000) return x-x;		/* NaN */

    /* argument reduction needed */
	else {
	    n = __ieee754_rem_pio2(x,y);
	    return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /*   1 -- n even
							-1 -- n odd */
	}
}

**** End of s_tan.c. ****

**** Start of s_tanh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)s_tanh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* Tanh(x)
 * Return the Hyperbolic Tangent of x
 *
 * Method :
 *				       x    -x
 *				      e  - e
 *	0. tanh(x) is defined to be -----------
 *				       x    -x
 *				      e  + e
 *	1. reduce x to non-negative by tanh(-x) = -tanh(x).
 *	2.  0      <= x <= 2**-55 : tanh(x) := x*(one+x)
 *					        -t
 *	    2**-55 <  x <=  1     : tanh(x) := -----; t = expm1(-2x)
 *					       t + 2
 *						     2
 *	    1      <= x <=  22.0  : tanh(x) := 1-  ----- ; t=expm1(2x)
 *						   t + 2
 *	    22.0   <  x <= INF    : tanh(x) := 1.
 *
 * Special cases:
 *	tanh(NaN) is NaN;
 *	only tanh(0)=0 is exact for finite argument.
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double one=1.0, two=2.0, tiny = 1.0e-300;
#else
static double one=1.0, two=2.0, tiny = 1.0e-300;
#endif

#ifdef __STDC__
	double fd_tanh(double x)
#else
	double fd_tanh(x)
	double x;
#endif
{
	double t,z;
	int jx,ix;

    /* High word of |x|. */
	jx = __HI(x);
	ix = jx&0x7fffffff;

    /* x is INF or NaN */
	if(ix>=0x7ff00000) { 
	    if (jx>=0) return one/x+one;    /* tanh(+-inf)=+-1 */
	    else       return one/x-one;    /* tanh(NaN) = NaN */
	}

    /* |x| < 22 */
	if (ix < 0x40360000) {		/* |x|<22 */
	    if (ix<0x3c800000) 		/* |x|<2**-55 */
		return x*(one+x);    	/* tanh(small) = small */
	    if (ix>=0x3ff00000) {	/* |x|>=1  */
		t = fd_expm1(two*fd_fabs(x));
		z = one - two/(t+two);
	    } else {
	        t = fd_expm1(-two*fd_fabs(x));
	        z= -t/(t+two);
	    }
    /* |x| > 22, return +-1 */
	} else {
	    z = one - tiny;		/* raised inexact flag */
	}
	return (jx>=0)? z: -z;
}

**** End of s_tanh.c. ****

**** Start of w_acos.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_acos.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * wrap_acos(x)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_acos(double x)		/* wrapper acos */
#else
	double fd_acos(x)			/* wrapper acos */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_acos(x);
#else
	double z;
	z = __ieee754_acos(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(fd_fabs(x)>1.0) {
	        return __kernel_standard(x,x,1); /* acos(|x|>1) */
	} else
	    return z;
#endif
}

**** End of w_acos.c. ****

**** Start of w_acosh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_acosh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* 
 * wrapper acosh(x)
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_acosh(double x)		/* wrapper acosh */
#else
	double fd_acosh(x)			/* wrapper acosh */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_acosh(x);
#else
	double z;
	z = __ieee754_acosh(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(x<1.0) {
	        return __kernel_standard(x,x,29); /* acosh(x<1) */
	} else
	    return z;
#endif
}

**** End of w_acosh.c. ****

**** Start of w_asin.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_asin.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* 
 * wrapper asin(x)
 */


#include "fdlibm.h"


#ifdef __STDC__
	double fd_asin(double x)		/* wrapper asin */
#else
	double fd_asin(x)			/* wrapper asin */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_asin(x);
#else
	double z;
	z = __ieee754_asin(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(fd_fabs(x)>1.0) {
	        return __kernel_standard(x,x,2); /* asin(|x|>1) */
	} else
	    return z;
#endif
}

**** End of w_asin.c. ****

**** Start of w_atan2.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_atan2.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* 
 * wrapper atan2(y,x)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_atan2(double y, double x)	/* wrapper atan2 */
#else
	double fd_atan2(y,x)			/* wrapper atan2 */
	double y,x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_atan2(y,x);
#else
	double z;
	z = __ieee754_atan2(y,x);
	if(_LIB_VERSION == _IEEE_||fd_isnan(x)||fd_isnan(y)) return z;
	if(x==0.0&&y==0.0) {
	        return __kernel_standard(y,x,3); /* atan2(+-0,+-0) */
	} else
	    return z;
#endif
}

**** End of w_atan2.c. ****

**** Start of w_atanh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_atanh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */
/* 
 * wrapper atanh(x)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_atanh(double x)		/* wrapper atanh */
#else
	double fd_atanh(x)			/* wrapper atanh */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_atanh(x);
#else
	double z,y;
	z = __ieee754_atanh(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	y = fd_fabs(x);
	if(y>=1.0) {
	    if(y>1.0)
	        return __kernel_standard(x,x,30); /* atanh(|x|>1) */
	    else 
	        return __kernel_standard(x,x,31); /* atanh(|x|==1) */
	} else
	    return z;
#endif
}

**** End of w_atanh.c. ****

**** Start of w_cosh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_cosh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper cosh(x)
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_cosh(double x)		/* wrapper cosh */
#else
	double fd_cosh(x)			/* wrapper cosh */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_cosh(x);
#else
	double z;
	z = __ieee754_cosh(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(fd_fabs(x)>7.10475860073943863426e+02) {	
	        return __kernel_standard(x,x,5); /* cosh overflow */
	} else
	    return z;
#endif
}

**** End of w_cosh.c. ****

**** Start of w_exp.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_exp.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper exp(x)
 */

#include "fdlibm.h"

#ifdef __STDC__
static const double
#else
static double
#endif
o_threshold=  7.09782712893383973096e+02,  /* 0x40862E42, 0xFEFA39EF */
u_threshold= -7.45133219101941108420e+02;  /* 0xc0874910, 0xD52D3051 */

#ifdef __STDC__
	double fd_exp(double x)		/* wrapper exp */
#else
	double fd_exp(x)			/* wrapper exp */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_exp(x);
#else
	double z;
	z = __ieee754_exp(x);
	if(_LIB_VERSION == _IEEE_) return z;
	if(fd_finite(x)) {
	    if(x>o_threshold)
	        return __kernel_standard(x,x,6); /* exp overflow */
	    else if(x<u_threshold)
	        return __kernel_standard(x,x,7); /* exp underflow */
	} 
	return z;
#endif
}

**** End of w_exp.c. ****

**** Start of w_fmod.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_fmod.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper fmod(x,y)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_fmod(double x, double y)	/* wrapper fmod */
#else
	double fd_fmod(x,y)		/* wrapper fmod */
	double x,y;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_fmod(x,y);
#else
	double z;
	z = __ieee754_fmod(x,y);
	if(_LIB_VERSION == _IEEE_ ||fd_isnan(y)||fd_isnan(x)) return z;
	if(y==0.0) {
	        return __kernel_standard(x,y,27); /* fmod(x,0) */
	} else
	    return z;
#endif
}

**** End of w_fmod.c. ****

**** Start of w_gamma.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_gamma.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* double gamma(double x)
 * Return the logarithm of the Gamma function of x.
 *
 * Method: call gamma_r
 */

#include "fdlibm.h"

extern int signgam;

#ifdef __STDC__
	double fd_gamma(double x)
#else
	double fd_gamma(x)
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_gamma_r(x,&signgam);
#else
        double y;
        y = __ieee754_gamma_r(x,&signgam);
        if(_LIB_VERSION == _IEEE_) return y;
        if(!fd_finite(y)&&fd_finite(x)) {
            if(fd_floor(x)==x&&x<=0.0)
                return __kernel_standard(x,x,41); /* gamma pole */
            else
                return __kernel_standard(x,x,40); /* gamma overflow */
        } else
            return y;
#endif
}             

**** End of w_gamma.c. ****

**** Start of w_gamma_r.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_gamma_r.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper double gamma_r(double x, int *signgamp)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_gamma_r(double x, int *signgamp) /* wrapper lgamma_r */
#else
	double fd_gamma_r(x,signgamp)              /* wrapper lgamma_r */
        double x; int *signgamp;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_gamma_r(x,signgamp);
#else
        double y;
        y = __ieee754_gamma_r(x,signgamp);
        if(_LIB_VERSION == _IEEE_) return y;
        if(!fd_finite(y)&&fd_finite(x)) {
            if(fd_floor(x)==x&&x<=0.0)
                return __kernel_standard(x,x,41); /* gamma pole */
            else
                return __kernel_standard(x,x,40); /* gamma overflow */
        } else
            return y;
#endif
}             

**** End of w_gamma_r.c. ****

**** Start of w_hypot.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_hypot.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * wrapper hypot(x,y)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_hypot(double x, double y)/* wrapper hypot */
#else
	double fd_hypot(x,y)		/* wrapper hypot */
	double x,y;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_hypot(x,y);
#else
	double z;
	z = __ieee754_hypot(x,y);
	if(_LIB_VERSION == _IEEE_) return z;
	if((!fd_finite(z))&&fd_finite(x)&&fd_finite(y))
	    return __kernel_standard(x,y,4); /* hypot overflow */
	else
	    return z;
#endif
}

**** End of w_hypot.c. ****

**** Start of w_j0.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_j0.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * wrapper j0(double x), y0(double x)
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_j0(double x)		/* wrapper j0 */
#else
	double fd_j0(x)			/* wrapper j0 */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_j0(x);
#else
	double z = __ieee754_j0(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(fd_fabs(x)>X_TLOSS) {
	        return __kernel_standard(x,x,34); /* j0(|x|>X_TLOSS) */
	} else
	    return z;
#endif
}

#ifdef __STDC__
	double y0(double x)		/* wrapper y0 */
#else
	double y0(x)			/* wrapper y0 */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_y0(x);
#else
	double z;
	z = __ieee754_y0(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z;
        if(x <= 0.0){
                if(x==0.0)
                    /* d= -one/(x-x); */
                    return __kernel_standard(x,x,8);
                else
                    /* d = zero/(x-x); */
                    return __kernel_standard(x,x,9);
        }
	if(x>X_TLOSS) {
	        return __kernel_standard(x,x,35); /* y0(x>X_TLOSS) */
	} else
	    return z;
#endif
}

**** End of w_j0.c. ****

**** Start of w_j1.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_j1.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper of j1,y1 
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_j1(double x)		/* wrapper j1 */
#else
	double fd_j1(x)			/* wrapper j1 */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_j1(x);
#else
	double z;
	z = __ieee754_j1(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z;
	if(fd_fabs(x)>X_TLOSS) {
	        return __kernel_standard(x,x,36); /* j1(|x|>X_TLOSS) */
	} else
	    return z;
#endif
}

#ifdef __STDC__
	double y1(double x)		/* wrapper y1 */
#else
	double y1(x)			/* wrapper y1 */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_y1(x);
#else
	double z;
	z = __ieee754_y1(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z;
        if(x <= 0.0){
                if(x==0.0)
                    /* d= -one/(x-x); */
                    return __kernel_standard(x,x,10);
                else
                    /* d = zero/(x-x); */
                    return __kernel_standard(x,x,11);
        }
	if(x>X_TLOSS) {
	        return __kernel_standard(x,x,37); /* y1(x>X_TLOSS) */
	} else
	    return z;
#endif
}

**** End of w_j1.c. ****

**** Start of w_jn.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_jn.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * wrapper jn(int n, double x), yn(int n, double x)
 * floating point Bessel's function of the 1st and 2nd kind
 * of order n
 *          
 * Special cases:
 *	y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal;
 *	y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal.
 * Note 2. About jn(n,x), yn(n,x)
 *	For n=0, j0(x) is called,
 *	for n=1, j1(x) is called,
 *	for n<x, forward recursion us used starting
 *	from values of j0(x) and j1(x).
 *	for n>x, a continued fraction approximation to
 *	j(n,x)/j(n-1,x) is evaluated and then backward
 *	recursion is used starting from a supposed value
 *	for j(n,x). The resulting value of j(0,x) is
 *	compared with the actual value to correct the
 *	supposed value of j(n,x).
 *
 *	yn(n,x) is similar in all respects, except
 *	that forward recursion is used for all
 *	values of n>1.
 *	
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_jn(int n, double x)	/* wrapper jn */
#else
	double fd_jn(n,x)			/* wrapper jn */
	double x; int n;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_jn(n,x);
#else
	double z;
	z = __ieee754_jn(n,x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z;
	if(fd_fabs(x)>X_TLOSS) {
	    return __kernel_standard((double)n,x,38); /* jn(|x|>X_TLOSS,n) */
	} else
	    return z;
#endif
}

#ifdef __STDC__
	double yn(int n, double x)	/* wrapper yn */
#else
	double yn(n,x)			/* wrapper yn */
	double x; int n;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_yn(n,x);
#else
	double z;
	z = __ieee754_yn(n,x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x) ) return z;
        if(x <= 0.0){
                if(x==0.0)
                    /* d= -one/(x-x); */
                    return __kernel_standard((double)n,x,12);
                else
                    /* d = zero/(x-x); */
                    return __kernel_standard((double)n,x,13);
        }
	if(x>X_TLOSS) {
	    return __kernel_standard((double)n,x,39); /* yn(x>X_TLOSS,n) */
	} else
	    return z;
#endif
}

**** End of w_jn.c. ****

**** Start of w_lgamma.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_lgamma.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 *
 */

/* double lgamma(double x)
 * Return the logarithm of the Gamma function of x.
 *
 * Method: call __ieee754_lgamma_r
 */

#include "fdlibm.h"

extern int signgam;

#ifdef __STDC__
	double fd_lgamma(double x)
#else
	double fd_lgamma(x)
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_lgamma_r(x,&signgam);
#else
        double y;
        y = __ieee754_lgamma_r(x,&signgam);
        if(_LIB_VERSION == _IEEE_) return y;
        if(!fd_finite(y)&&fd_finite(x)) {
            if(fd_floor(x)==x&&x<=0.0)
                return __kernel_standard(x,x,15); /* lgamma pole */
            else
                return __kernel_standard(x,x,14); /* lgamma overflow */
        } else
            return y;
#endif
}             

**** End of w_lgamma.c. ****

**** Start of w_lgamma_r.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_lgamma_r.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper double lgamma_r(double x, int *signgamp)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_lgamma_r(double x, int *signgamp) /* wrapper lgamma_r */
#else
	double fd_lgamma_r(x,signgamp)              /* wrapper lgamma_r */
        double x; int *signgamp;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_lgamma_r(x,signgamp);
#else
        double y;
        y = __ieee754_lgamma_r(x,signgamp);
        if(_LIB_VERSION == _IEEE_) return y;
        if(!fd_finite(y)&&fd_finite(x)) {
            if(fd_floor(x)==x&&x<=0.0)
                return __kernel_standard(x,x,15); /* lgamma pole */
            else
                return __kernel_standard(x,x,14); /* lgamma overflow */
        } else
            return y;
#endif
}             

**** End of w_lgamma_r.c. ****

**** Start of w_log.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_log.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * wrapper log(x)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_log(double x)		/* wrapper log */
#else
	double fd_log(x)			/* wrapper log */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_log(x);
#else
	double z;
	z = __ieee754_log(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x) || x > 0.0) return z;
	if(x==0.0)
	    return __kernel_standard(x,x,16); /* log(0) */
	else 
	    return __kernel_standard(x,x,17); /* log(x<0) */
#endif
}

**** End of w_log.c. ****

**** Start of w_log10.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_log10.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper log10(X)
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_log10(double x)		/* wrapper log10 */
#else
	double fd_log10(x)			/* wrapper log10 */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_log10(x);
#else
	double z;
	z = __ieee754_log10(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(x<=0.0) {
	    if(x==0.0)
	        return __kernel_standard(x,x,18); /* log10(0) */
	    else 
	        return __kernel_standard(x,x,19); /* log10(x<0) */
	} else
	    return z;
#endif
}

**** End of w_log10.c. ****

**** Start of w_pow.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */


/* @(#)w_pow.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper pow(x,y) return x**y
 */

#include "fdlibm.h"


#ifdef __STDC__
	double fd_pow(double x, double y)	/* wrapper pow */
#else
	double fd_pow(x,y)			/* wrapper pow */
	double x,y;
#endif
{
#ifdef _IEEE_LIBM
	return  __ieee754_pow(x,y);
#else
	double z;
	z=__ieee754_pow(x,y);
	if(_LIB_VERSION == _IEEE_|| fd_isnan(y)) return z;
	if(fd_isnan(x)) {
	    if(y==0.0) 
	        return __kernel_standard(x,y,42); /* pow(NaN,0.0) */
	    else 
		return z;
	}
	if(x==0.0){ 
	    if(y==0.0)
	        return __kernel_standard(x,y,20); /* pow(0.0,0.0) */
	    if(fd_finite(y)&&y<0.0)
	        return __kernel_standard(x,y,23); /* pow(0.0,negative) */
	    return z;
	}
	if(!fd_finite(z)) {
	    if(fd_finite(x)&&fd_finite(y)) {
	        if(fd_isnan(z))
	            return __kernel_standard(x,y,24); /* pow neg**non-int */
	        else 
	            return __kernel_standard(x,y,21); /* pow overflow */
	    }
	} 
	if(z==0.0&&fd_finite(x)&&fd_finite(y))
	    return __kernel_standard(x,y,22); /* pow underflow */
	return z;
#endif
}

**** End of w_pow.c. ****

**** Start of w_remainder.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_remainder.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper remainder(x,p)
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_remainder(double x, double y)	/* wrapper remainder */
#else
	double fd_remainder(x,y)			/* wrapper remainder */
	double x,y;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_remainder(x,y);
#else
	double z;
	z = __ieee754_remainder(x,y);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(y)) return z;
	if(y==0.0) 
	    return __kernel_standard(x,y,28); /* remainder(x,0) */
	else
	    return z;
#endif
}

**** End of w_remainder.c. ****

**** Start of w_scalb.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_scalb.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/*
 * wrapper scalb(double x, double fn) is provide for
 * passing various standard test suite. One 
 * should use scalbn() instead.
 */

#include "fdlibm.h"

#include <errno.h>

#ifdef __STDC__
#ifdef _SCALB_INT
	double fd_scalb(double x, int fn)		/* wrapper scalb */
#else
	double fd_scalb(double x, double fn)	/* wrapper scalb */
#endif
#else
	double fd_scalb(x,fn)			/* wrapper scalb */
#ifdef _SCALB_INT
	double x; int fn;
#else
	double x,fn;
#endif
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_scalb(x,fn);
#else
	double z;
	z = __ieee754_scalb(x,fn);
	if(_LIB_VERSION == _IEEE_) return z;
	if(!(fd_finite(z)||fd_isnan(z))&&fd_finite(x)) {
	    return __kernel_standard(x,(double)fn,32); /* scalb overflow */
	}
	if(z==0.0&&z!=x) {
	    return __kernel_standard(x,(double)fn,33); /* scalb underflow */
	} 
#ifndef _SCALB_INT
	if(!fd_finite(fn)) errno = ERANGE;
#endif
	return z;
#endif 
}

**** End of w_scalb.c. ****

**** Start of w_sinh.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_sinh.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper sinh(x)
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_sinh(double x)		/* wrapper sinh */
#else
	double fd_sinh(x)			/* wrapper sinh */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_sinh(x);
#else
	double z; 
	z = __ieee754_sinh(x);
	if(_LIB_VERSION == _IEEE_) return z;
	if(!fd_finite(z)&&fd_finite(x)) {
	    return __kernel_standard(x,x,25); /* sinh overflow */
	} else
	    return z;
#endif
}

**** End of w_sinh.c. ****

**** Start of w_sqrt.c. ****

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Sun Microsystems,
 * Inc.  Portions created by Netscape are Copyright (C) 1998 Netscape
 * Communications Corporation.  All Rights Reserved.
 */

/* @(#)w_sqrt.c 1.3 95/01/18 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice 
 * is preserved.
 * ====================================================
 */

/* 
 * wrapper sqrt(x)
 */

#include "fdlibm.h"

#ifdef __STDC__
	double fd_sqrt(double x)		/* wrapper sqrt */
#else
	double fd_sqrt(x)			/* wrapper sqrt */
	double x;
#endif
{
#ifdef _IEEE_LIBM
	return __ieee754_sqrt(x);
#else
	double z;
	z = __ieee754_sqrt(x);
	if(_LIB_VERSION == _IEEE_ || fd_isnan(x)) return z;
	if(x<0.0) {
	    return __kernel_standard(x,x,26); /* sqrt(negative) */
	} else
	    return z;
#endif
}

**** End of w_sqrt.c. ****
